2015-10-13 89 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