web-dev-qa-db-ja.com

関数の最後にデバッガブレークポイントを戻さずに設定する

returnが含まれていないメソッドf()をデバッグしています。

class A(object):

    def __init__(self):
        self.X = []

    def f(self):            
        for i in range(10):
            self.X.append(i)

このメソッドが呼び出された直後に変数Xをどのように変更するかを確認する必要があります。これを行うには、メソッドの最後にreturnを挿入し、そこにブレークポイントを設定します。

enter image description here

そうすれば、メソッドがreturnに達するとすぐに、変数Xの値を確認できます。


これでうまくいきますが、もっと良い方法があると確信しています。デバッグが必要になるたびにメソッドや関数を編集するのはばかげているようです。

質問
returnを持たないメソッドの最後にブレークポイントを設定する別の方法(デバッガーのオプションなど)はありますか?

(関数呼び出しでブレークポイントを設定し、を使用することに注意してください Step Over 関数は別のモジュールから呼び出されるため、マウスオーバー時にXは表示されません。)

36
Fermi paradox

最後の行に条件付きブレークポイントを追加し、最後の反復でのみ発生する条件に設定できます。

この場合、条件はi == 9であるため非常に簡単ですが、ループ条件によってははるかに複雑になる可能性があるため、最後にステートメントを追加する方が簡単な解決策になる場合があります。

Conditional breakpoint in IntelliJ IDEA

そのスクリーンショットはIntelliJ IDEAからのものであり、スクリーンショットは同じIDEからのもののように見えるので、ブレークポイントを右クリックしてダイアログを表示し、条件を入力します。

他のIDEを使用している場合は、ブレークポイントを条件付きにする機能があると確信しています。

更新:

Pythonデバッガー のメソッドの最後でのブレークはサポートされていません。メソッドの開始時のみです。

b(reak)[[filename:] lineno |関数[、条件]]

Lineno引数を使用して、現在のファイルにブレークを設定します。関数の引数を使用して、その関数内の最初の実行可能ステートメントにブレークを設定します。別のファイル(おそらくまだロードされていないファイル)のブレークポイントを指定するために、行番号の前にファイル名とコロンを付けることができます。ファイルはsys.pathで検索されます。各ブレークポイントには、他のすべてのブレークポイントコマンドが参照する番号が割り当てられていることに注意してください。

2番目の引数が存在する場合、それはブレークポイントが受け入れられる前にtrueと評価される必要がある式です。

引数なしで、各ブレークポイント、ブレークポイントがヒットした回数、現在の無視カウント、および関連する条件(存在する場合)を含むすべてのブレークをリストします。

6
Raniz

あなたのIDEは、内部にあるものを隠しています。つまり、次のようなものです。

_import pdb
_

スクリプトの前に追加され、

_pdb.set_trace()
_

ブレークポイントを配置した行の前に挿入されます。あなたの言うことから、PyCharmは空の行にブレークポイントを置くのが好きではないと推測します。ただし、pdb.set_trace()はメソッドの最後に完全に配置できます。

したがって、それらを自分で挿入(またはマクロを作成)し、_python -m pdb_を実行してデバッグを開始できます。

(編集)例

_import pdb

class A(object):

    def __init__(self):
        self.X = []

    def f(self):
        for i in range(10):
            self.X.append(i)
        pdb.set_trace()

if __name__ == '__main__':
    a = A()
    a.f()
_

でデバッグ

_$ python -m pdb test.py 
> /dev/test.py(1)<module>()
----> 1 import pdb
      2 
      3 class A(object):

ipdb> cont
--Return--
> /dev/test.py(11)f()->None
-> pdb.set_trace()
(Pdb) self.X
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
(Pdb)
_

ipdbの代わりにpdbを使用できます。

6

モンキーパッチをサポートするany言語(Python、Ruby、ObjCなど)で動作するquick&dirtyソリューションがあります。正直なところ、Pythonで必要になったのを覚えていませんが、SmalltalkとObjCの両方でかなり行ったので、役立つかもしれません。

次のように、_A.f_を関数で動的にラップします。

_real_A_f = A.f
def wrap_A_f(self, *args, **kwargs):
    result = real_A_f(self, *args, **kwargs)
    return result
A.f = wrap_A_f
_

ほとんどのスクリプト可能なデバッガーでは、名前でメソッドに対してこれを自動的に行うスクリプトを記述できるはずです。通常のPythonコードをデバッガーで直接実行できるpdbでは、特に簡単です。

これで、その_return result_にブレークポイントを設定でき、実際の_A.f_が戻った直後にヒットすることが保証されます(途中で戻ったり、returnなしで最後から外れたりした場合でも)ステートメント)。

追加したいことがいくつかあります。

  • _A.f_のレイズもキャッチしたい場合は、コードの周りに_try:_と_except: raise_を置き、raiseにブレークポイントを追加します。
  • Python 2.xの場合、types.MethodTypeでラップして、実際のバインドされていないメソッドを作成することをお勧めします。
  • 特定のAインスタンスにのみブレークポイントが必要な場合は、_self is a_をチェックする条件付きブレークポイントを使用するか、_types.MethodType_を使用してバインドされたインスタンスを作成し、それを_a.f_。
  • ラッパーをコードの残りの部分から(そして、本当に見たい場合を除いて、デバッグから)非表示にする場合は、_functools.wraps_を使用することをお勧めします。
  • Pdbを使用すると、ライブ名前空間で動的コードを直接実行できるため、これを実行する_wrap_method_関数をプロジェクトのどこかに配置し、プロンプトでp utils.wrap_method(A, 'f')と記述できます。ただし、この方法で複数のメソッドをラップすると、それらは同じブレークポイントを共有します(_wrap_method_内で定義されたラッパー関数内)。ここでは、条件付きブレークポイントが唯一の合理的なオプションだと思います。
  • ラッパーのブレークポイントから実際のA.fのローカルにアクセスしたい場合、それははるかに困難です。非常にハッキーなオプション(exec(real_A_f.__code__, real_A_f.globals())など)を思いつくことができますが、満足できるものは何もありません。
4
abarnert

pdb を使用すると、 _break function__until lineno_ の組み合わせを使用できます。

引数なしで、現在の行よりも大きい数の行に到達するまで実行を続行します。

行番号を使用して、それ以上の番号の行に到達するまで実行を続行します。どちらの場合も、現在のフレームが戻ったときに停止します。

バージョン3.2で変更:明示的な行番号の指定を許可します。

あなたはあなたが必要なことを達成することができます。

例を少し変更しました(pdbが「次の命令」として報告しても、命令が実行されることがわかります):

_01: class A(object):
02: 
03:     def __init__(self):
04:         self.X = []
05:         
06:     def f(self):         
07:         print('pre exec')
08:         for i in range(10):
09:             self.X.append(i)
10:         print('post exec')
11:             
12: a = A()
13: a.f()
14: print('Game is over')
15:
_

そして、_python -m pdb test.py_で実行した結果は次のようになります。

デバッグを開始し、クラス宣言の直後に実行します(名前付きブレークポイントを追加できるようにします)。

_> d:\tmp\stack\test.py(1)<module>()
-> class A(object):
(Pdb) until 11
> d:\tmp\stack\test.py(12)<module>()
-> a = A()
_

ここで、関数の最初で中断します。

_(Pdb) break A.f
Breakpoint 1 at d:\tmp\stack\test.py:6
_

ブレークポイントに達するまで実行を続行します。

_(Pdb) continue
> d:\tmp\stack\test.py(7)f()
-> print('pre exec')
_

"現在のフレームが戻ったときにも停止する"を利用する:

_(Pdb) until 14
pre exec
post exec
--Return--
_

ご覧のとおり、pre execpost execの両方が出力されました。しかし、whereを実行すると、まだf()にいます。

_(Pdb) w
  c:\python32\lib\bdb.py(405)run()
-> exec(cmd, globals, locals)
  <string>(1)<module>()
  d:\tmp\stack\test.py(13)<module>()
-> a.f()
> d:\tmp\stack\test.py(10)f()->None
-> print('post exec')
> d:\tmp\stack\test.py(10)f()->None
-> print('post exec')
_

そして、すべてのコンテキスト変数はそのままです。

_(Pdb) p self.X
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_

今あなたの実際の例で:

_01: class A(object):
02:     def __init__(self):
03:         self.X = []
04:         
05:     def f(self):         
06:         for i in range(10):
07:             self.X.append(i)
08:             
09: a = A()
10: a.f()
11: print('Game is over')
_

以前と同様の方法で開始します。

_> d:\tmp\stack\test.py(1)<module>()
-> class A(object):
(Pdb) until 8
> d:\tmp\stack\test.py(9)<module>()
-> a = A()
(Pdb) break A.f
Breakpoint 1 at d:\tmp\stack\test.py:5
(Pdb) cont
> d:\tmp\stack\test.py(6)f()
-> for i in range(10):
_

さて..._f.A_のブレークポイントは、実際には_f.A_の最初のステートメントでのブレークポイントを意味します。これは残念ながら_for i in..._であるため、毎回ブレークポイントになります。

実際に実際のコードをループで開始しない場合は、この部分をスキップできます。

_(Pdb) disable 1
Disabled breakpoint 1 at d:\tmp\stack\test.py:5
_

繰り返しますが、_until <end of file>_を使用します。

_(Pdb) until 10
--Return--
> d:\tmp\stack\test.py(6)f()->None
-> for i in range(10):
_

また、すべてのフレーム変数が使用可能です。

_(Pdb) p i
9
(Pdb) w
  c:\python32\lib\bdb.py(405)run()
-> exec(cmd, globals, locals)
  <string>(1)<module>()
  d:\tmp\stack\test.py(10)<module>()
-> a.f()
> d:\tmp\stack\test.py(6)f()->None
-> for i in range(10):
(Pdb)
_

ここでの悲しいことは、この自動化を試してみたかったことです。

_(Pdb) break A.f
Breakpoint 1 at d:\tmp\stack\test.py:5
(Pdb) commands 1
(com) disable 1
(com) until 11
(com) end
_

これは、必要なすべてを自動的に実行します(ここでも、少なくとも1つのプレループステートメントがある場合は_disable 1_は必要ありません)が、 commands のドキュメントによると:

実行を再開するコマンド(現在、continue、step、next、return、jump、quit、およびそれらの省略形)を指定すると、コマンドリストが終了します(そのコマンドの直後にendが続くかのように)。これは、実行を再開するたびに(単純な次またはステップでも)、別のブレークポイントが発生する可能性があるためです。このブレークポイントには独自のコマンドリストがあり、実行するリストがあいまいになる可能性があります。

したがって、untilは機能していないようです(少なくともPython 3.2.5ではWindowsの場合))。これは手動で行う必要があります。

4
Vyktor

ここにはいくつかのオプションがあります。

  1. 関数の最後の行にブレークポイントを追加します。

この場合、最後の行はループ内にあるため、ループ内の各アイテムを反復処理する必要があります。

  1. 関数が呼び出されている場所にブレークポイントを追加します。

これにより、関数が呼び出される前にデバッガーが停止しますが、関数を「ステップオーバー」して、A.f()が呼び出された後にA.xの値を確認できます。

  1. ブレークする関数の最後に一時的なステートメントを追加します

このトリックは、関数がループで終了し、関数が呼び出される場所が複数ある場合、または関数呼び出しを追跡したくない場合に機能します。

デバッグの目的で関数の最後に簡単なステートメントを追加し、そこにブレークポイントを追加できます。

def f(self):            
    for i in range(10):
        self.X.append(i)
    debug_break = 1
1
pferate

リターンをそのままにしておかないのはなぜですか?またはreturn None。とにかくそれは暗黙的です、インタプリタ/コンパイラは関係なく同じことをします:

実際、returnステートメントのない関数でさえ、かなり退屈なものではありますが、値を返します。この値はNoneと呼ばれます(組み込みの名前です)。

[ソース:Pythonチュートリアル4.6]

1
skolsuper