2012-11-15 154 views
11

有一個大的python項目,其中一個類的一個屬性在某個地方只有錯誤的值。監視python中的變量變化

應該sqlalchemy.orm.attributes.InstrumentedAttribute,但是當我運行測試中,它是恆定值,讓我們說字符串。

有一些方法來在調試模式下運行python程序,並運行一些檢查每個步驟throught自動行代碼後(如果變量改變型)?

P.S.我知道如何藉助檢查和屬性裝飾器記錄類實例屬性的更改。也許在這裏我可以用這個方法使用元類...

但有時我需要更多的通用和強大的解決方案...

謝謝。

P.P.S.我需要這樣的東西:https://stackoverflow.com/a/7669165/816449,但可能會更多地解釋代碼中發生了什麼。

回答

11

嗯,這裏是一個排序緩慢方法。它可以被修改用於觀察局部變量變化(僅通過名稱)。以下是它的工作方式:我們執行sys.settrace並分析obj.attr每一步的值。棘手的部分是我們在執行行之前收到'line'事件(某行已執行)。所以,當我們注意到obj.attr已經改變時,我們已經在下一行,並且我們不能得到以前的行框架(因爲沒有爲每一行復制框架,它們被修改)。因此,在每一行事件中,我將traceback.format_stack保存爲watcher.prev_st,如果在下次調用trace_command時值已更改,我們將保存的堆棧跟蹤打印到文件。在每一行上保存回溯操作都是相當昂貴的操作,因此您必須將include關鍵字設置爲您的項目目錄列表(或者僅僅是項目的根目錄),以免看到其他庫如何處理它們的內容和浪費中央處理器。

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

您可以使用python debugger module(標準庫的一部分)

使用,只需在你的源文件的頂部導入PDB:

import pdb 

,然後設置一個跟蹤,無論你想開始檢查代碼:

pdb.set_trace() 

然後,您可以通過與n步執行代碼,並通過運行蟒蛇COM調查當前狀態mands。

+1

對不起,我忘了補充,我想這個想法自動工作。所以我開始調試器,給它我的條件,例如鍵入(some.module.SomeClass.my_attribute)== str),並找到條件不符合的第一行。 而且有數百萬行代碼,我不知道變量在哪裏改變。 – Bunyk

1

嘗試使用__setattr__Documentation__setattr__

+0

有一點需要注意 - 這隻適用於定義'__setattr__'的類的實例的屬性。要將它與類屬性一起使用,我們需要爲類重新定義元類,並且誰知道我們需要使用模塊中定義的變量來使用它。 – Bunyk

+0

只是一個建議。 –