PEP-3134:例外チェーンと埋め込みトレースバック
Python 3.0では、例外オブジェクトに新たに__context__、__cause__、__traceback__の3つの属性が追加されました。__context__属性は、もともと別の例外の処理中に発生した例外を明示的に発生させるときに役立ちます。Python 2.xでは、次に示すように元の例外が失われ、2番目の例外に置き換わっていました。
>>> try:
... raise Exception('first')
... except:
... raise Exception('second')
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
Exception: second
Python 3.0で同じコードを実行すると次のようになります。
>>> try:
... raise Exception('first')
... except:
... raise Exception('second')
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
Exception: first
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
Exception: second
このように、2番目の例外オブジェクトが元の例外オブジェクトを__context__属性に格納します。
>>> e.__context__
Exception('first',)
>>> type(e.__context__)
<class 'Exception'>
ネストされたtry-exceptブロックがある場合は、この例外チェーンの処理を次々に続けることができます。それぞれの例外が、その前の例外を__context__に格納します。
__cause__属性は、ある例外を別の例外に明示的に変換するときに役立ちます。構文はraise EXCEPTION from CAUSEです。CAUSEも例外でなければなりません。次に例を示します。
try:
raise Exception('duh!') from Exception('Gigi')
except Exception as e:
assert e.__context__ is None
assert repr(e.__cause__) == "Exception('Gigi',)"
私には、この構文が必要な理由がよく分かりません。from CAUSEはシンタックスシュガーにすぎません。CAUSEが__context__に格納されることを除けば、これは次の構文と同等だからです。
try:
try:
raise CAUSE
except:
raise EXCEPTION
一方の形式では元の例外が__context__に格納され、もう一方の形式では__cause__に格納されると、まぎらわしくて複雑なエラー処理コードを生む可能性があります。どうなるのかわからないので、捕捉された例外だけでなく、__cause__と__context__も確認する必要があります。シンタックスシュガーのfrom CAUSEはともかくとして、CAUSEは__context__に格納されるべきだと思います。
Exceptionのもう1つの新しい属性が__traceback__で、これは例外の原因を知らせるトレースバックオブジェクトです。Python 2.xでは、sys.exc_info()[2]またはsys.exc_tracebackを呼び出してトレースバックオブジェクトを取得する必要がありました。それが例外オブジェクト自体の属性となったため、以前より見つけやすくなり、コードもより簡潔になります。トレースバックオブジェクトの意味を理解できるように、tracebackモジュールを忘れずに使用してください。
import traceback
def f():
g()
def g():
raise Exception('nested')
t = None
try:
f()
except Exception as e:
t = e.__traceback__
print('----------------')
print('The stack trace')
print('----------------')
traceback.print_tb(t)
print('----------------')
----------------
The stack trace
----------------
File "article_1.py", line 244, in <module>
f()
File "article_1.py", line 237, in f
g()
File "article_1.py", line 240, in g
raise Exception('nested')
----------------
まとめ
この『Python 3.0開発者ガイド』シリーズの最初の記事では、コア言語と型システムの重要な変更点について説明しました。シリーズの次の記事では、数値、テキスト、バイナリデータの各基本データ型がPython 3.0でどのように扱われるかに焦点を当てます。
