さて、これは一種のゆっくりとしたアプローチです。ローカル変数の変更を監視するために変更できます (名前のみ)。これがどのように機能するかです: sys.settrace を実行し、各ステップで obj.attr の値を分析します。'line'
注意が必要なのは、行が実行される前に (行が実行されたという) イベントを受け取ることです。そのため、obj.attr が変更されたことに気付いたとき、すでに次の行にいて、前の行のフレームを取得できません (フレームは行ごとにコピーされないため、変更されます)。traceback.format_stack
したがって、保存する各行イベントで、値watcher.prev_st
の次の呼び出しでtrace_command
変更された場合は、保存されたスタック トレースをファイルに出力します。各行にトレースバックを保存するのは非常にコストのかかる操作なので、設定する必要がありますinclude
キーワードをプロジェクト ディレクトリ (または単にプロジェクトのルート) のリストに追加して、他のライブラリがどのように処理を行っているかを監視して CPU を浪費しないようにします。
ウォッチャー.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()