#はじめに
皆さんはデバッグをどのように行っていますか?
デバッガとか使ってするのが一般的でしょうか、Pdb
とか?
それはさておき、エラー発生時ってとりあえず関数内のローカル変数を見たくなりますよね。
でも”ローカルな”変数ですので、関数外からは見られず結局print
に走る、あるあるだと思います。
今回は、せめてエラー発生時の変数だけでもまとめて出力できたら…と思いデコレータを作成しました。
#つくったもの
def raise_locals(f):
import sys, traceback
def wrapper(*args, **kwargs):
try:
return f(*args, **kwargs)
except Exception:
exc_type, exc_value, exc_traceback = sys.exc_info()
ext = traceback.StackSummary.extract(traceback.walk_tb(exc_traceback), capture_locals=True)
locals_ = ext[-1].locals
if locals_:
print('{' + ', '.join([f"'{k}': {v}" for k, v in locals_.items()]) + '}')
raise
return wrapper
構造としては、実行してみてエラーが発生したらtracebackを通じてローカル変数を拾ってくるって感じです。
では使ってみましょう。
@raise_locals
def f(x):
a = x
b = 1 / a
a = 1
f(0)
out
{'x': 0, 'a': 0}
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-137-16e1baa342f5> in <module>
5 a = 1
6
----> 7 f(0)
<ipython-input-136-f595b8b52afb> in wrapper(*args, **kwargs)
3 def wrapper(*args, **kwargs):
4 try:
----> 5 f(*args, **kwargs)
6 except Exception as e:
7 exc_type, exc_value, exc_traceback = sys.exc_info()
<ipython-input-137-16e1baa342f5> in f(x)
2 def f(x):
3 a = x
----> 4 b = 1 / a
5 a = 1
6
ZeroDivisionError: division by zero
いつものエラー文の前にエラー発生時のローカル変数が表示されていますね。
def f(x):
a = x
b = 1 / a
@raise_locals
def main():
f(x=0)
if __name__=='__main__':
main()
一番親となる関数につけておけば、どこでエラーが発生してもそのエラーが発生した関数のローカル変数を見ることができます。