16

1 つのクラスの 1 つの属性がどこかで間違った値を持つ大規模な python プロジェクトがあります。

sqlalchemy.orm.attributes.InstrumentedAttribute である必要がありますが、テストを実行すると定数値になります。文字列としましょう。

Pythonプログラムをデバッグモードで実行し、各ステップの後にコード行を自動的にチェックする(変数の型が変更された場合)いくつかの方法がありますか?

PSインスペクトとプロパティデコレータを使用して、クラスインスタンスの属性の変更をログに記録する方法を知っています。おそらくここで、このメソッドをメタクラスで使用できます...

しかし、より一般的で強力なソリューションが必要な場合もあります...

ありがとうございました。

PPS私はそこのようなものが必要です: https://stackoverflow.com/a/7669165/816449、しかしそのコードで何が起こっているのかについてのより多くの説明があるかもしれません。

4

6 に答える 6

13

さて、これは一種のゆっくりとしたアプローチです。ローカル変数の変更を監視するために変更できます (名前のみ)。これがどのように機能するかです: 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()
于 2012-11-15T19:40:06.133 に答える
0

Python デバッガ モジュール(標準ライブラリの一部) を使用できます。

使用するには、ソース ファイルの先頭に pdb をインポートするだけです。

import pdb

次に、コードの検査を開始する場所にトレースを設定します。

pdb.set_trace()

次に、 を使用してコードをステップn実行し、python コマンドを実行して現在の状態を調査できます。

于 2012-11-15T17:33:45.313 に答える