2012-11-15 3 views
11

큰 파이썬 프로젝트가 있습니다. 하나의 클래스의 속성 하나가 잘못된 값을 가지고있는 곳이 있습니다.파이썬에서 변수 변경을 확인하십시오

sqlalchemy.orm.attributes.InstrumentedAttribute가되어야합니다. 그러나 테스트를 실행하면 상수 값이됩니다. 문자열을 봅시다.

디버그 모드에서 파이썬 프로그램을 실행하고 코드 단계마다 자동으로 실행 한 후 일부 검사 (변수가 변경된 경우)를 실행하는 방법이 있습니까?

P. 검사 및 속성 데코레이터의 도움으로 클래스 인스턴스의 속성 변경을 기록하는 방법을 알고 있습니다. 아마도 나는 여기 ... 메타 클래스와

을이 방법을 사용할 수 있습니다하지만 가끔은 내가

감사합니다 ... 더 일반적이고 강력한 솔루션이 필요합니다.

P.P.S. 거기에는 다음과 같은 것이 필요합니다 : https://stackoverflow.com/a/7669165/816449, 그 코드에서 어떤 일이 벌어지고 있는지 더 자세히 설명해 줄 수 있습니다.

답변

11

글쎄, 여기에 일종의 느림 접근 방식이 있습니다. 로컬 변수 변경 (이름 만)을 감시하기 위해 수정할 수 있습니다. 작동 원리는 다음과 같습니다. sys.settrace를 수행하고 각 단계의 obj.attr 값을 분석합니다. 까다로운 부분은 행이 실행되기 전에 'line' 이벤트 (일부 행이 실행 되었음)를 수신한다는 것입니다. 따라서 obj.attr이 변경되었다는 것을 알게되면 이미 다음 줄에 있습니다. 이전 줄 프레임을 가져올 수 없습니다 (각 줄마다 프레임이 복사되지 않으므로 수정됩니다). 따라서 각 라인 이벤트에서 traceback.format_stackwatcher.prev_st으로 저장하고 다음 호출시 trace_command 값이 변경되면 저장된 스택 추적을 파일로 인쇄합니다. 각 라인에 추적을 저장하는 것은 꽤 비싼 작업입니다. 따라서 include 키워드를 프로젝트 디렉토리 목록 (또는 프로젝트의 루트)으로 설정하여 다른 라이브러리가 자신의 물건을 어떻게 낭비하고 낭비 하는지를 보지 않아야합니다 cpu.

watcher.py

import traceback 

class Watcher(object): 
    def __init__(self, obj=None, attr=None, log_file='log.txt', include=[], enabled=False): 
     """ 
      Debugger that watches for changes in object attributes 
      obj - object to be watched 
      attr - string, name of attribute 
      log_file - string, where to write output 
      include - list of strings, debug files only in these directories. 
       Set it to path of your project otherwise it will take long time 
       to run on big libraries import and usage. 
     """ 

     self.log_file=log_file 
     with open(self.log_file, 'wb'): pass 
     self.prev_st = None 
     self.include = [incl.replace('\\','/') for incl in include] 
     if obj: 
      self.value = getattr(obj, attr) 
     self.obj = obj 
     self.attr = attr 
     self.enabled = enabled # Important, must be last line on __init__. 

    def __call__(self, *args, **kwargs): 
     kwargs['enabled'] = True 
     self.__init__(*args, **kwargs) 

    def check_condition(self): 
     tmp = getattr(self.obj, self.attr) 
     result = tmp != self.value 
     self.value = tmp 
     return result 

    def trace_command(self, frame, event, arg): 
     if event!='line' or not self.enabled: 
      return self.trace_command 
     if self.check_condition(): 
      if self.prev_st: 
       with open(self.log_file, 'ab') as f: 
        print >>f, "Value of",self.obj,".",self.attr,"changed!" 
        print >>f,"###### Line:" 
        print >>f,''.join(self.prev_st) 
     if self.include: 
      fname = frame.f_code.co_filename.replace('\\','/') 
      to_include = False 
      for incl in self.include: 
       if fname.startswith(incl): 
        to_include = True 
        break 
      if not to_include: 
       return self.trace_command 
     self.prev_st = traceback.format_stack(frame) 
     return self.trace_command 
import sys 
watcher = Watcher() 
sys.settrace(watcher.trace_command) 

testwatcher.py

from watcher import watcher 
import numpy as np 
import urllib2 
class X(object): 
    def __init__(self, foo): 
     self.foo = foo 

class Y(object): 
    def __init__(self, x): 
     self.xoo = x 

    def boom(self): 
     self.xoo.foo = "xoo foo!" 
def main(): 
    x = X(50) 
    watcher(x, 'foo', log_file='log.txt', include =['C:/Users/j/PycharmProjects/hello']) 
    x.foo = 500 
    x.goo = 300 
    y = Y(x) 
    y.boom() 
    arr = np.arange(0,100,0.1) 
    arr = arr**2 
    for i in xrange(3): 
     print 'a' 
     x.foo = i 

    for i in xrange(1): 
     i = i+1 

main() 
+0

예, 이것은 매우 느리지 만 수동 pdb보다 빠릅니다. 감사합니다. – Bunyk

+0

예, 수정 됨. 그건 그렇고, 당신이 실제가 아닌 다음 라인을 사용해도 괜찮다면, 훨씬 더 빠른 방법으로 이것을 할 수 있습니다. https://gist.github.com/4086770 다음 라인이나 실제'line' 이벤트가'line' 이벤트 다음에 오는 지 여부에 따라 달라집니다. –

1

당신은

은 소스 파일의 상단에 PDB를 가져, 사용하려면 python debugger module (표준 라이브러리의 일부)를 사용할 수 있습니다 : 당신이 원하는 목적지

import pdb 

후 추적을 설정

pdb.set_trace() 

그런 다음 n와 코드를 단계별로 및 COM 파이썬을 실행하여 현재 상태를 조사 할 수 있습니다 : 코드를 검사 시작 mands.

+1

죄송합니다. 추가하는 것을 잊어 버렸습니다. 자동으로 작동하고 싶습니다. 그래서 디버거를 시작합니다. 예를 들어, (some.module.SomeClass.my_attribute) == str)을 입력하고 조건이 충족되지 않은 첫 번째 줄을 찾습니다. 그리고 수백만 줄의 코드가 있으며 변수가 변경되는 위치를 알지 못합니다. – Bunyk

1

__setattr__를 사용해보십시오. Documentation (__setattr__)

+0

여기에 주목해야 할 것이 있습니다 - 이것은'__setattr__'을 정의하는 클래스의 인스턴스 속성에서만 작동합니다. 클래스 속성과 함께 사용하려면 클래스의 메타 클래스를 다시 정의해야하며 모듈에 정의 된 변수를 사용하여 작동하게 만드는 데 필요한 마술을 알고 있어야합니다. – Bunyk

+0

그냥 제안. –

관련 문제