2015-10-13 90 views
0

我正在嘗試調試使用第三方包的多線程程序。如何在調試時檢測實例數據屬性更改?

在某個時候,某個對象的屬性(不是由我直接創建)的一個屬性發生了變化,我無法弄清楚是什麼改變了它。我找不到直接改變它的代碼。

由於這是第三方軟件包,我不想直接更改其代碼,而是根據需要從外部修補它。

我的計劃是以某種方式利用或包裝代碼來設置它並設置斷點或從那裏打印堆棧跟蹤。

我試着猴子補丁實例的__setattr__方法,但它沒有被觸發。

我也試圖平息類本身:

def patch_class(target): 

    def method(self, name, value): 
     print(name, value) 
     print("called from", target) 
     setattr(self, name, value) # break or print trace here 

    target.__setattr__ = types.MethodType(method, target) 

patch_class(WebSocket) 

但隨後所有的屬性都在類本身設置,由於該方法被綁定到它。

用代理包裝類也沒有什麼幫助,因爲我沒有自己實例化它,而是在它創建後的某個時候訪問實例。

如果有問題,說上課是ws4pyWebSocket是由另一個第三方包創建的,但我認爲這是一般性調試技巧的練習。

是否有更多的「pythonic」方式攻入現有實例的變種(hack-ish的方式也將被讚賞)?

+0

怎麼樣一個屬性的getter和setter,然後在setter中調試? –

+0

好吧,我不想更改軟件包代碼。我設法使用'wrapt.ObjectProxy'做一些事情,但我想知道是否有另一種方法。一旦它穩定下來,我會發布它。 – MasterAM

回答

0

我最終爲該課程創建了一個__setattr__

def setter_fun(self, name, value): 
    print('setting', name, value) 
    self.__dict__[name] = value 
    if name is 'problematic_prop' and value is 'problematicValue': 
     traceback.print_stack() 

# and set the class setter magic method 
instance.__class__.__setattr__ = setter_fun 

也可以使用setattr而不是使用__dict__魔法屬性:

setattr(self, name, value) 

現在,當一些設置實例的problematic_propproblematicValue,堆棧跟蹤將被打印出來:

>>> class A(object): 
     def __init__(self): 
      self.foo = 1 

     def set_problematic(self): 
      self.problematic_prop = 'problematicValue' 
>>> a = A() 
>>> a.__class__.__setattr__ = setter_fun 
>>> a.foo = 2 
setting foo 2 
>>> print(a.foo) 
2 
>>> a.set_problematic() 
setting problematic_prop problematicValue 
Traceback (most recent call last): 
    File "<input>", line 1, in <module> 
    File "<input>", line 6, in set_problematic 
    File "<input>", line 5, in setter_fun 
NameError: name 'traceback' is not defined 

我失敗的嘗試包括嘗試將__setattr__附加到實例而不是類,或試圖附加一個綁定的方法:

class MyClass(object): 
    def setter_fun(self, name, value): 
     print('setting', name, value) 
     self.__dict__[name] = value 
     if name is 'problematic_prop' and value is 'problematicValue': 
      traceback.print_stack() 

    def set_my_function(self): 
     # won't work, the function is bound to the current instance (self) 
     some.instace.__class__.__setattr__ = self.setter_fun