Pythonでの雑でガバっとした例外処理

定期的に動かすので、ある程度きちんとプログラムを書かないといけないけど、そんなに丁寧に例外処理を書いている余裕はないぞというケースがある。

そういう時、実行スクリプトでは、このように雑に例外処理を行うことが多い。 こうしておけば、とりあえずどんな例外もログに吐き出すことができ、未知の例外に遭遇した時はエラーメッセージを見ながら、適宜コードを修正することができる。

import traceback


try:
    a()
    b()
    c()
    d()
    e()
except:
    msg = traceback.format_exc()
    mail_logger.error(msg)

ところでDB周りの業務をやっていると、SQLAlchemyを使って、下のようにsession_scope関数を定義し、色んなところで使いまわす。

from contextlib import contextmanager


@contextmanager
def session_scope():
    session = Session()
    try:
        yield session
        session.commit()
    except:
        session.rollback()
    finally:
        session.close()

# session_scopeはこのように使う
def insert(something):
    with session_scope() as session:
        session.add(something)
# メインの処理はこのような感じ
try:
    something = Something()
    insert(something)
except:
    msg = traceback.format_exc()
    mail_logger.error(msg)

しかし、このsession_scopeを上記のようなtry句内で使うと、DB操作で例外が発生した時に、session_scope内のexceptfinnalyでプログラムが終了し、肝心の最上位のexcept句に処理が到達しない。 こうなると、ログが吐き出されないので、困ったことになる。

こういうときは、session_scope内のexcept句の中で例外を再スローしてやるとよい。 そうすると、きちんと処理が最上位のexceptにまで到達し、DB操作時に例外が発生した時の状況がログに書き込まれる。

from exceptions import DBHandleException


@contextmanager
def session_scope():
    session = Session()
    try:
        yield session
        session.commit()
    except:
        session.rollback()
        # 例外を再スローする
        raise DBHandleException
    finally:
        session.close()