再 raise

この記事は Python Tips Advent Calendar 2012 8日目の記事です。


Python で受けた例外をそのまま上に伝える場合、単に raise と書くだけで伝えられます。

>>> try:        #doctest: +ELLIPSIS
...     raise Exception, 'error'
... except Exception, e:
...     if e.message == 'error':
...         raise
Traceback (most recent call last):
    ...
Exception: error

これは、以下のように書いたのと同じです。

>>> import sys
>>> try:        #doctest: +ELLIPSIS
...     raise Exception, 'error'
... except Exception, e:
...     if e.message == 'error':
...         info = sys.exc_info()
...         raise info[0], info[1], info[2]
Traceback (most recent call last):
    ...
Exception: error

raise の引数はそれぞれ、例外の型を表すオブジェクト、例外オブジェクトに渡す引数、トレースバック情報となります。


単に raise e と思うかもしれませんが、以下のようになってしまいます。

>>> def f3():
...     raise Exception, 'error'
>>> def f2():
...     try:
...         f3()
...     except Exception, e:
...         raise e
>>> def f1():
...     try:
...         f2()
...     except Exception, e:
...         raise e
>>> f1()        #doctest: +ELLIPSIS
Traceback (most recent call last):
  File "....py", line 1254, in __run
    compileflags, 1) in test.globs
  File "<doctest __main__[3]>", line 1, in <module>
    f1()
  File "<doctest __main__[2]>", line 5, in f1
    raise e
Exception: error

実際に例外を投げたのは f3 関数ですが、トレースバック情報を見てみると、f1 関数で例外が投げられたことになっています。


raise を使えば、

>>> def f3():
...     raise Exception, 'error'
... 
>>> def f2():
...     try:
...         f3()
...     except Exception, e:
...         raise
... 
>>> def f1():
...     try:
...         f2()
...     except Exception, e:
...         raise
... 
>>> f1()        #doctest: +ELLIPSIS
Traceback (most recent call last):
  File "....py", line 1254, in __run
    compileflags, 1) in test.globals
  File "<doctest __main__[7]>", line 1, in <module>
    f1()
  File "<doctest __main__[6]>", line 3, in f1
    f2()
  File "<doctest __main__[5]>", line 3, in f2
    f3()
  File "<doctest __main__[4]>", line 2, in f3
    raise Exception, 'error'
Exception: error

このように f3 で例外が投げられたことが分かります。


ということで、ちゃんと raise を使う、あるいはトレースバック情報(sys.exc_info()[2])をちゃんと入れるようにしましょう。


.