如何在没有dunder setattr或pdb的情况下监视python中的变量更改 [英] How to watch for a variable change in python without dunder setattr or pdb

查看:104
本文介绍了如何在没有dunder setattr或pdb的情况下监视python中的变量更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有一个大型的python项目,其中一个类的一个属性在某个地方只是错误的值。

There is large python project where one attribute of one class just have wrong value in some place.

应为sqlalchemy.orm.attributes.InstrumentedAttribute,但是当我运行测试它是一个常量值,比如说字符串。

It should be sqlalchemy.orm.attributes.InstrumentedAttribute, but when I run tests it is constant value, let's say string.

有一些方法可以在调试模式下运行python程序,然后在运行后执行一些检查(如果变量的类型改变了)

There is some way to run python program in debug mode, and run some check (if variable changed type) after each step throught line of code automatically?

PS我知道如何在检查和属性装饰器的帮助下记录类实例的属性更改。可能在这里我可以对元类使用此方法...

P.S. I know how to log changes of attribute of class instance with help of inspect and property decorator. Possibly here I can use this method with metaclasses...

但是有时候我需要更通用,更强大的解决方案...

But sometimes I need more general and powerfull solution...

谢谢。

PPS我需要类似的内容: https://stackoverflow.com/a/7669165/816449 ,但可能会有更多解释

P.P.S. I need something like there: https://stackoverflow.com/a/7669165/816449, but may be with more explanation of what is going on in that code.

推荐答案

嗯,这是一种 slow 方法。可以对其进行修改以监视局部变量的更改(仅按名称)。它是这样工作的:我们执行sys.settrace并在每个步骤中分析obj.attr的值。棘手的是,在执行行之前,我们会收到'line'事件(执行了某些行)。因此,当我们注意到obj.attr发生了变化时,我们已经在下一行,并且无法获得前一行的帧(因为没有为每行复制帧,所以它们被修改了)。因此,在每个线路事件中,我将 traceback.format_stack 保存到 watcher.prev_st ,如果在下一次调用 trace_command 的值已更改,我们将保存的堆栈跟踪打印到文件中。在每一行上保存回溯是一项相当昂贵的操作,因此您必须在 include 关键字中设置项目目录的列表(或只是项目的根目录)。

Well, here is a sort of slow approach. It can be modified for watching for local variable change (just by name). Here is how it works: we do sys.settrace and analyse the value of obj.attr each step. The tricky part is that we receive 'line' events (that some line was executed) before line is executed. So, when we notice that obj.attr has changed, we are already on the next line and we can't get the previous line frame (because frames aren't copied for each line, they are modified ). So on each line event I save traceback.format_stack to watcher.prev_st and if on the next call of trace_command value has changed, we print the saved stack trace to file. Saving traceback on each line is quite an expensive operation, so you'd have to set include keyword to a list of your projects directories (or just the root of your project) in order not to watch how other libraries are doing their stuff and waste 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)

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()

这篇关于如何在没有dunder setattr或pdb的情况下监视python中的变量更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆