web-dev-qa-db-ja.com

Pythonデバッグ:関数が呼び出されるファイル名と行番号を取得しますか?

現在、Pythonで非常に複雑なシステムを構築しています。デバッグしているときは、いくつかのスクリプトに単純な印刷ステートメントを挿入することがよくあります。概要を維持するために、printステートメントが置かれているファイル名と行番号も印刷したいことがよくあります。もちろん、手動で、または次のような方法でそれを行うことができます。

from inspect import currentframe, getframeinfo
print getframeinfo(currentframe()).filename + ':' + str(getframeinfo(currentframe()).lineno) + ' - ', 'what I actually want to print out here'

これは次のようなものを出力します:

filenameX.py:273-ここで実際に印刷したいもの

もっと簡単にするために、私は次のようなことができるようにしたいと思います:

print debuginfo(), 'what I actually want to print out here'

だから私はどこかにそれを関数に入れてやってみました:

from debugutil import debuginfo
print debuginfo(), 'what I actually want to print out here'
print debuginfo(), 'and something else here'

残念ながら、私は得ます:

debugutil.py:3 - what I actually want to print out here
debugutil.py:3 - and something else here

Debuginfo()を呼び出す行ではなく、関数を定義したファイル名と行番号を出力します。コードはdebugutil.pyファイルにあるため、これは明らかです。

だから私の質問は実際に:このdebuginfo()関数が呼び出されるファイル名と行番号を取得するにはどうすればよいですか?すべてのヒントは大歓迎です!

34
kramer65

関数 inspect.stack() は、発信者から開始して移動する フレームレコード のリストを返します。これを使用して、必要な情報を取得できます。

from inspect import getframeinfo, stack

def debuginfo(message):
    caller = getframeinfo(stack()[1][0])
    print "%s:%d - %s" % (caller.filename, caller.lineno, message)

def grr(arg):
    debuginfo(arg)

grr("aargh")

出力

example.py:8 - aargh
51
Zero Piraeus

トレースコードを別の関数に配置し、それをメインコードから呼び出す場合は、親やトレース関数自体ではなく祖父母からスタック情報を取得する必要があります。

以下は、私が何を意味するかをさらに明確にするための3レベルの深いシステムの例です。私のメイン関数はトレース関数を呼び出し、トレース関数はさらに別の関数を呼び出して作業を行います。

######################################

import sys, os, inspect, time
time_start = 0.0                    # initial start time

def trace_libary_init():
    global time_start

    time_start = time.time()      # when the program started

def trace_library_do(relative_frame, msg=""):
    global time_start

    time_now = time.time()

        # relative_frame is 0 for current function (this one), 
        # 1 for direct parent, or 2 for grand parent.. 

    total_stack         = inspect.stack()                   # total complete stack
    total_depth         = len(total_stack)                  # length of total stack
    frameinfo           = total_stack[relative_frame][0]    # info on rel frame
    relative_depth      = total_depth - relative_frame      # length of stack there

        # Information on function at the relative frame number

    func_name           = frameinfo.f_code.co_name
    filename            = os.path.basename(frameinfo.f_code.co_filename)
    line_number         = frameinfo.f_lineno                # of the call
    func_firstlineno    = frameinfo.f_code.co_firstlineno

    fileline            = "%s:%d" % (filename, line_number)
    time_diff           = time_now - time_start

    print("%13.6f %-20s %-24s %s" % (time_diff, fileline, func_name, msg))

################################

def trace_do(msg=""):
    trace_library_do(1, "trace within interface function")
    trace_library_do(2, msg)
    # any common tracing stuff you might want to do...

################################

def main(argc, argv):
    rc=0
    trace_libary_init()
    for i in range(3):
        trace_do("this is at step %i" %i)
        time.sleep((i+1) * 0.1)         # in 1/10's of a second
    return rc

rc=main(sys.argv.__len__(), sys.argv)
sys.exit(rc)

これは次のようなものを出力します:

$ python test.py 
    0.000005 test.py:39           trace_do         trace within interface func
    0.001231 test.py:49           main             this is at step 0
    0.101541 test.py:39           trace_do         trace within interface func
    0.101900 test.py:49           main             this is at step 1
    0.302469 test.py:39           trace_do         trace within interface func
    0.302828 test.py:49           main             this is at step 2

先頭にあるtrace_library_do()関数は、ライブラリにドロップして、他のトレース関数から呼び出すことができるものの例です。相対深度値は、pythonスタックのどのエントリが出力されるかを制御します。

関数の開始の行番号、合計スタック深度、ファイルへの完全パスなど、その関数の他のいくつかの興味深い値を引き出していることを示しました。表示しませんでしたが、関数内のグローバル変数とローカル変数も、inspectで利用できます。また、自分の関数の下にある他のすべての関数への完全なスタックトレースも利用できます。階層的なコール/リターンタイミングトレースを作成するために、上記で示したものには十分な情報があります。実際にここから独自のソースレベルデバッガーの主要部分を作成するのはそれほど遠くはありません。ほとんどの場合、そこに座って使用されるのを待っています。

これと同じことをするアクセス関数があるかもしれないので、誰かが検査構造によって返されたデータで内部フィールドを使用していることに異議を唱えると確信しています。しかし、私はpythonデバッガーでこのタイプのコードをステップ実行することによってそれらを見つけました、そしてそれらは少なくともここで動作します。私はpython 2.7.12を実行しています。 、別のバージョンを実行している場合、結果は非常に大きくなる可能性があります。

いずれの場合でも、検査コードをいくつかのpython独自のコードにインポートし、それが何を提供できるかを確認することを強くお勧めします。特に、良いpythonデバッガ。pythonがどのように機能するかについて多くを学び、言語の利点と背後で何が起こっているかを理解するそれを可能にするカーテン。

タイムスタンプを使用した完全なソースレベルのトレースは、特に動的なリアルタイム環境において、コードが何をしているかについて理解を深める優れた方法です。この種類のトレースコードの優れた点は、いったん作成すると、それを表示するためにデバッガのサポートが不要になることです。

3
pdb

あなたが投稿したコードを関数に入れるだけです:

from inspect import currentframe, getframeinfo

def my_custom_debuginfo(message):
    print getframeinfo(currentframe()).filename + ':' + str(getframeinfo(currentframe()).lineno) + ' - ', message

そしてそれをあなたが望むように使ってください:

# ... some code here ...
my_custom_debuginfo('what I actually want to print out here')
# ... more code ...

その関数を別のモジュールに入れることをお勧めします。そうすることで、必要なときにいつでも再利用できます。

0
Raydel Miranda

やや関連する問題についてこの質問を発見しましたが、実行に関する詳細を求めていました(そして、コールグラフパッケージ全体をインストールしたくありませんでした)。

より詳細な情報が必要な場合は、標準ライブラリモジュール traceback を使用して完全なトレースバックを取得し、スタックオブジェクト(タプルのリスト)をtraceback.extract_stack()で隠しておくか、出力しますtraceback.print_stack()で出力します。これは私のニーズにより適していました。他の人の役に立つことを願っています!

0
CrepeGoat