10

次のような例外にコンテキストを追加したいと思います。

def process(vals):
    for key in vals:
        try:
            do_something(vals[key])
        except Exception as ex:  # base class. Not sure what to expect.
            raise # with context regarding the key that was being processed.

私は、Python にとって特徴のない長い道のりを見つけました。これよりも良い方法はありますか?

try:
    do_something(vals[key])
except Exception as ex:
    args = list(ex.args)
    if len(args) > 1:
        args[0] = "{}: {}".format(key, args[0])
        ex.args = tuple(args)
    raise # Will re-trhow ValueError with new args[0]
4

2 に答える 2

6

の最初の項目ex.argsは、常にメッセージです (存在する場合)。( によって発生するものなど、一部の例外は空のタプルであることに注意してください。assert False)ex.args

に新しいタプルを再割り当てするよりも、メッセージを変更するクリーンな方法を知りませんex.args。(タプルは不変であるため、タプルを変更することはできません)。

以下のコードはあなたのものと似ていますが、中間リストを使用せずにタプルを構築し、ex.argsが空の場合にケースを処理し、コードを読みやすくするために、ボイラープレートをコンテキスト マネージャー内に隠しています。

import contextlib

def process(val):
    with context(val):
        do_something(val)

def do_something(val):
    # assert False
    return 1/val

@contextlib.contextmanager
def context(msg):
    try:
        yield
    except Exception as ex:
        msg = '{}: {}'.format(msg, ex.args[0]) if ex.args else str(msg)
        ex.args = (msg,) + ex.args[1:]
        raise

process(0)

これを最終メッセージとしてスタック トレースを生成します。

ZeroDivisionError: 0: division by zero
于 2013-07-16T13:31:22.077 に答える
2

新しい例外を発生させることができます:

def process(vals):
    for key in vals:
        try:
            do_something(vals[key])
        except Exception as ex:  
            raise Error(key, context=ex)

Python 3 では、古い例外を明示的に提供する必要はありません__context__。新しい例外オブジェクトの属性として使用でき、デフォルトの例外ハンドラーが自動的に報告します。

def process(vals):
    for key in vals:
        try:
            do_something(vals[key])
        except Exception:  
            raise Error(key)

あなたの場合、おそらく新しい例外に属性raise Error(key) from exを設定する明示的な構文を使用する必要があります。 Exception Chaining and Embedded Tracebacksを参照してください。__cause__


唯一の問題が、質問のメッセージ修正コードの冗長性である場合。関数にカプセル化できます。

try:
    do_something(vals[key])
except Exception:
    reraise_with_context(key=key) # reraise with extra info

どこ:

import inspect
import sys

def reraise_with_context(**context):
    ex = sys.exc_info()[1]
    if not context: # use locals from the caller scope
       context = inspect.currentframe().f_back.f_locals
    extra_info = ", ".join("%s=%s" % item for item in context.items())
    amend_message(ex, extra_info)
    raise

def amend_message(ex, extra):
    msg = '{} with context: {}'.format(ex.args[0], extra) if ex.args else extra
    ex.args = (msg,) + ex.args[1:]
于 2013-07-16T14:29:44.307 に答える