例外処理(try-except)を実装する際、「その場でエラーを解決する」のではなく、「エラーが発生したという事実をログに記録し、実際の対処は呼び出し元(上位の処理)に任せたい」というケースがあります。
このような場合に有効なのが、**例外の再送出(Re-raising)**です。一度捕捉した例外を、意図的に再度発生させることで、エラー情報を握りつぶさずに上位へ伝播させることができます。
この記事では、raise 文を使用した例外の再送出方法と、トレースバック(エラーの発生箇所情報)を正しく維持するための記述方法について解説します。
例外の再送出とは
try-except ブロックで例外を捕捉すると、通常そのエラーはそこで「解決済み」とみなされ、プログラムは後続の処理を続行します。
しかし、関数内で発生した致命的なエラーなどは、関数内部だけで処理を完結させず、関数を呼び出した側に「エラーが起きた」ことを通知する必要があります。この時、except ブロック内で raise 文を使用します。
正しい再送出の方法:引数なしの raise
例外を再送出する場合、最も推奨される方法は、引数を指定せずに単独で raise と記述することです。
構文:
try:
# 処理
except Exception:
# ログ出力などの処理
raise # 捕捉した例外をそのまま再送出する
具体的な使用例
設定ファイルの値を読み込み、数値に変換する関数を例にします。変換に失敗した場合、エラーログを出力した上で、システム全体に異常を知らせるために例外を再送出します。
def parse_configuration(config_value):
"""
設定値を数値に変換する関数
"""
try:
# 文字列を整数に変換(失敗すると ValueError)
result = int(config_value)
return result
except ValueError:
# 1. ここでエラーを検知し、ログを出力する
print(f"[Log] 設定値の変換に失敗しました。値: {config_value}")
# 2. 例外を再送出する(呼び出し元にエラーを通知)
raise
# --- メイン処理 ---
print("処理を開始します。")
try:
# 不正な値(文字列)を渡して関数を実行
value = parse_configuration("InvalidNumber")
except ValueError:
# 3. 呼び出し元で再送出された例外を捕捉する
print("[System] 設定エラーのため処理を中断します。")
print("処理を終了します。")
実行結果:
処理を開始します。
[Log] 設定値の変換に失敗しました。値: InvalidNumber
[System] 設定エラーのため処理を中断します。
処理を終了します。
この流れにより、「関数内でのログ出力」と「呼び出し元でのエラー対応(中断処理)」の両方が実現されています。
raise e と raise の違い
例外オブジェクトを変数で受け取り、raise e のように記述して再送出することも可能です。
except ValueError as e:
print("ログ出力")
raise e # 変数を指定して再送出
しかし、単に元の例外をそのまま上に投げるだけであれば、引数なしの raise が推奨されます。
raise(引数なし): 発生した例外の「トレースバック(エラーが発生した行番号などの履歴)」をそのまま維持して送出します。デバッグ時に、最初のエラー発生箇所が明確になります。raise e: Pythonのバージョンや状況によっては、トレースバック情報が上書きされ、どこで最初のエラーが起きたのかが追いにくくなる場合があります(Python 3では連鎖例外として扱われますが、意図がない限りraise単独の方がシンプルで副作用がありません)。
まとめ
- 例外を捕捉した後、ログ出力や一時的な後始末を行い、エラー自体は上位の処理に任せたい場合に「再送出」を使用します。
exceptブロック内で単にraiseと記述することで、現在捕捉している例外をそのまま再送出できます。- これにより、エラーの発生箇所(トレースバック)を正確に保ったまま、エラー情報を伝播させることができます。
