Bun*_*nyk 15 python debugging introspection pdb
有一个庞大的python项目,其中一个类的一个属性在某些地方只有错误的值.
它应该是sqlalchemy.orm.attributes.InstrumentedAttribute,但是当我运行测试时它是常量值,让我们说字符串.
有一些方法可以在调试模式下运行python程序,并在每个步骤之后自动执行一些检查(如果变量更改类型)吗?
PS我知道如何在inspect和property decorator的帮助下记录类实例属性的变化.可能在这里我可以使用这种方法与元类...
但有时我需要更一般和更强大的解决方案......
谢谢.
PPS我需要类似的东西:https://stackoverflow.com/a/7669165/816449,但可能会更详细地解释该代码中发生的事情.
ale*_*dan 12
嗯,这是一种缓慢的方法.可以修改它以查看局部变量(仅按名称).以下是它的工作原理:我们执行sys.settrace并分析每一步的obj.attr值.棘手的部分是我们'line'在执行行之前接收事件(某些行被执行).因此,当我们注意到obj.attr已经改变时,我们已经在下一行,并且我们无法获得前一个行帧(因为没有为每一行复制帧,它们被修改).因此,在每个行事件中,我保存traceback.format_stack到watcher.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)
Run Code Online (Sandbox Code Playgroud)
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()
Run Code Online (Sandbox Code Playgroud)
有一个非常简单的方法可以做到这一点:使用观察点。
基本上你只需要做
from watchpoints import watch
watch(your_object.attr)
Run Code Online (Sandbox Code Playgroud)
就是这样。每当属性更改时,它都会打印出更改它的行以及更改方式。超级容易使用。
它还具有更高级的功能,例如,您可以在变量更改时调用 pdb,或者使用您自己的回调函数而不是将其打印到 stdout。
| 归档时间: |
|
| 查看次数: |
16664 次 |
| 最近记录: |