Ответ 1
Ну, вот что-то вроде медленного подхода. Он может быть изменен для просмотра локальной переменной (только по имени). Вот как это работает: мы делаем 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()