我想将上下文添加到这样的异常中:
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]
答案 0 :(得分:4)
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
答案 1 :(得分: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)
在您的情况下,您应该使用在新异常上设置__cause__
属性的显式raise Error(key) from ex
语法,请参阅Exception Chaining and Embedded Tracebacks。
如果唯一的问题是问题的详细程度 - 修改问题中的代码;你可以将它封装在一个函数中:
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:]