銀月の符号

Python 使い見習いの日記・雑記

traceback の情報を残しつつ別の型の例外を送出

触発されて、例外の再送出について調べてみました。

例外を捕らえたものの処理しきらないで再送出する場合、 methane さんの指摘の通り、情報量の少ない自作例外にしてしまうのは最悪です。しかし、 raise e としても traceback 情報がなくなってしまうため、これでもダメです。こういうときにはただ raise と書きます。

しかし、 Language Reference によると自作例外など別の型の例外を送出しつつ、 traceback の情報を残すことも可能ということがわかりました。 Python 2 系では raise 文の 3 つめの値に traceback オブジェクトを渡します。 Python 3 系では raise 文の後ろに from 節をつけ、これに例外オブジェクトを添えます。

情報源

ただの raise で足りていて困ったことが無いため、この機能をどういう状況で使うべきなのかは不明ですが…。以下、 Python 2.5.2 on win32 での実験コードです。

import sys
import traceback

class MyZeroDivisionError(ZeroDivisionError):
    pass

def zero():
    return 1/0

def ng1():
    try:
        zero()
    except ZeroDivisionError, e:
        raise MyZeroDivisionError('division by zero')

def ng2():
    try:
        zero()
    except ZeroDivisionError, e:
        raise e

def reraise():
    try:
        zero()
    except ZeroDivisionError, e:
        raise

def ok():
    try:
        zero()
    except ZeroDivisionError, e:
        t = sys.exc_info()[2]
        raise MyZeroDivisionError('division by zero'), None, t

funcs = [ng1, ng2, reraise, ok]

def main():
    for func in funcs:
        print func.__name__
        try:
            func()
        except:
            print traceback.format_exc()

if __name__ == '__main__':
    main()

出力です。

ng1
Traceback (most recent call last):
  File "S:\Python\test_traceback.py", line 41, in main
    func()
  File "S:\Python\test_traceback.py", line 14, in ng1
    raise MyZeroDivisionError('division by zero')
MyZeroDivisionError: division by zero

ng2
Traceback (most recent call last):
  File "S:\Python\test_traceback.py", line 41, in main
    func()
  File "S:\Python\test_traceback.py", line 20, in ng2
    raise e
ZeroDivisionError: integer division or modulo by zero

reraise
Traceback (most recent call last):
  File "S:\Python\test_traceback.py", line 41, in main
    func()
  File "S:\Python\test_traceback.py", line 24, in reraise
    zero()
  File "S:\Python\test_traceback.py", line 8, in zero
    return 1/0
ZeroDivisionError: integer division or modulo by zero

ok
Traceback (most recent call last):
  File "S:\Python\test_traceback.py", line 41, in main
    func()
  File "S:\Python\test_traceback.py", line 30, in ok
    zero()
  File "S:\Python\test_traceback.py", line 8, in zero
    return 1/0
MyZeroDivisionError: division by zero