Python 向异常添加信息?

Python 向异常添加信息?,python,exception,Python,Exception,我想实现这样的目标: def foo(): try: raise IOError('Stuff ') except: raise def bar(arg1): try: foo() except Exception as e: e.message = e.message + 'happens at %s' % arg1 raise bar('arg1') 回溯。。。 IOError('填充发生

我想实现这样的目标:

def foo():
   try:
       raise IOError('Stuff ')
   except:
       raise

def bar(arg1):
    try:
       foo()
    except Exception as e:
       e.message = e.message + 'happens at %s' % arg1
       raise

bar('arg1')
回溯。。。
IOError('填充发生在arg1')
但我得到的是:

回溯。。
IOError('Stuff')
关于如何做到这一点,有什么线索吗?如何在Python2和Python3中实现这一点?

也许吧

except Exception as e:
    raise IOError(e.message + 'happens at %s'%arg1)

您可以定义自己从另一个继承的异常,并创建自己的构造函数来设置值

例如:

class MyError(Exception):
   def __init__(self, value):
     self.value = value
     Exception.__init__(self)

   def __str__(self):
     return repr(self.value)

假设您不想或无法修改foo(),则可以执行以下操作:

try:
    raise IOError('stuff')
except Exception as e:
    if len(e.args) >= 1:
        e.args = (e.args[0] + ' happens',) + e.args[1:]
    raise

这确实是在Python3中解决问题的唯一解决方案,它没有一条丑陋而混乱的“在处理上述异常期间,发生了另一个异常”消息


如果需要将重新提升行添加到堆栈跟踪中,那么写入
raise e
而不是
raise
就可以了。

我会这样做,因此在
foo()
中更改其类型不需要在
bar()中更改它


回溯(最近一次呼叫最后一次):
文件“test.py”,第13行,在
条('arg1')
文件“test.py”,第11行,条形图
提升类型(e)(e.message+发生在%s“%arg1”)
IOError:在arg1发生了错误
更新1

以下是保留原始回溯的轻微修改:

...
def bar(arg1):
    try:
        foo()
    except Exception as e:
        import sys
        raise type(e), type(e)(e.message +
                               ' happens at %s' % arg1), sys.exc_info()[2]

bar('arg1')

回溯(最近一次呼叫最后一次):
文件“test.py”,第16行,在
条('arg1')
文件“test.py”,第11行,条形图
foo()
文件“test.py”,第5行,在foo中
引发IOError('Stuff'))
IOError:在arg1发生了错误
更新2

对于Python 3.x,我的第一次更新中的代码在语法上是不正确的,加上在
BaseException
上有
message
属性的想法是在2012-05-16(我的第一次更新发布于2012-03-12)。因此,在Python3.5.2中,您需要按照这些思路做一些事情来保留回溯,而不是在函数
bar()
中硬编码异常类型。还请注意,将有以下行:

在处理上述异常期间,发生了另一个异常:
在显示的回溯消息中

# for Python 3.x
...
def bar(arg1):
    try:
        foo()
    except Exception as e:
        import sys
        raise type(e)(str(e) +
                      ' happens at %s' % arg1).with_traceback(sys.exc_info()[2])

bar('arg1')
更新3

一位评论者问,是否有一种方法可以同时适用于Python2和Python3。尽管由于语法的不同,答案似乎是“否”,但有一种方法可以解决这个问题,即使用类似于附加模块中的helper函数。因此,如果您出于某种原因不想使用该库,下面是一个简化的独立版本

还要注意的是,由于异常是在
reraise()
函数中重新引发的,因此无论引发什么样的回溯,都会出现异常,但最终结果是您想要的

import sys

if sys.version_info.major < 3:  # Python 2?
    # Using exec avoids a SyntaxError in Python 3.
    exec("""def reraise(exc_type, exc_value, exc_traceback=None):
                raise exc_type, exc_value, exc_traceback""")
else:
    def reraise(exc_type, exc_value, exc_traceback=None):
        if exc_value is None:
            exc_value = exc_type()
        if exc_value.__traceback__ is not exc_traceback:
            raise exc_value.with_traceback(exc_traceback)
        raise exc_value

def foo():
    try:
        raise IOError('Stuff')
    except:
        raise

def bar(arg1):
    try:
       foo()
    except Exception as e:
        reraise(type(e), type(e)(str(e) +
                                 ' happens at %s' % arg1), sys.exc_info()[2])

bar('arg1')
导入系统 如果sys.version_info.major<3:#Python 2? #在Python3中使用exec可以避免语法错误。 exec(“”def reraise(exc_类型,exc_值,exc_回溯=无): 提升exc_类型、exc_值、exc_回溯“”) 其他: def reraise(exc_类型、exc_值、exc_回溯=无): 如果exc_值为无: exc_值=exc_类型() 如果exc_值。uu回溯_uuu不是exc_回溯: 提高exc_值。使用_回溯(exc_回溯) 提高exc_值 def foo(): 尝试: 引发IOError('Stuff')) 除: 提升 def条(arg1): 尝试: foo() 例外情况除外,如e: 重放(e型,e型)(str(e)+ '发生在%s“%arg1”、sys.exc_info()[2]) 条('arg1')
我使用的一种简便方法是使用class属性作为详细信息的存储,因为class属性可以从class对象和class实例中访问:

class CustomError(Exception):
    def __init__(self, details: Dict):
        self.details = details
然后在代码中:

raise CustomError({'data': 5})
捕获错误时:

except CustomError as e:
    # Do whatever you want with the exception instance
    print(e.details)

与之前的答案不同,这在异常情况下有效,异常情况非常糟糕
\uuuu str\uuuu
。 但是,它确实修改了类型,以便排除无用的
\uuu str\uu
实现

我仍然希望找到一个不修改类型的额外改进

from contextlib import contextmanager
@contextmanager
def helpful_info():
    try:
        yield
    except Exception as e:
        class CloneException(Exception): pass
        CloneException.__name__ = type(e).__name__
        CloneException.__module___ = type(e).__module__
        helpful_message = '%s\n\nhelpful info!' % e
        import sys
        raise CloneException, helpful_message, sys.exc_traceback


class BadException(Exception):
    def __str__(self):
        return 'wat.'

with helpful_info():
    raise BadException('fooooo')
保留原始回溯和类型(名称)

Traceback (most recent call last):
  File "re_raise.py", line 20, in <module>
    raise BadException('fooooo')
  File "/usr/lib64/python2.6/contextlib.py", line 34, in __exit__
    self.gen.throw(type, value, traceback)
  File "re_raise.py", line 5, in helpful_info
    yield
  File "re_raise.py", line 20, in <module>
    raise BadException('fooooo')
__main__.BadException: wat.

helpful info!
回溯(最近一次呼叫最后一次):
文件“re_raise.py”,第20行,在
引发异常('fooooo')
文件“/usr/lib64/python2.6/contextlib.py”,第34行,在__
self.gen.throw(类型、值、回溯)
文件“re_raise.py”,第5行,在帮助信息中
产量
文件“re_raise.py”,第20行,在
引发异常('fooooo')
__主要错误。例外情况:wat。
有用的信息!

每当我想向异常添加额外信息时,我都会提供一段我经常使用的代码。我同时使用Python 2.7和3.6

import sys
import traceback

try:
    a = 1
    b = 1j

    # The line below raises an exception because
    # we cannot compare int to complex.
    m = max(a, b)  

except Exception as ex:
    # I create my  informational message for debugging:
    msg = "a=%r, b=%r" % (a, b)

    # Gather the information from the original exception:
    exc_type, exc_value, exc_traceback = sys.exc_info()

    # Format the original exception for a nice printout:
    traceback_string = ''.join(traceback.format_exception(
        exc_type, exc_value, exc_traceback))

    # Re-raise a new exception of the same class as the original one, 
    # using my custom message and the original traceback:
    raise type(ex)("%s\n\nORIGINAL TRACEBACK:\n\n%s\n" % (msg, traceback_string))
上述代码产生以下输出:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-09b74752c60d> in <module>()
     14     raise type(ex)(
     15         "%s\n\nORIGINAL TRACEBACK:\n\n%s\n" %
---> 16         (msg, traceback_string))

TypeError: a=1, b=1j

ORIGINAL TRACEBACK:

Traceback (most recent call last):
  File "<ipython-input-6-09b74752c60d>", line 7, in <module>
    m = max(a, b)  # Cannot compare int to complex
TypeError: no ordering relation is defined for complex numbers
---------------------------------------------------------------------------
TypeError回溯(最近一次调用上次)
在()
14提升式(ex)(
15“%s\n\n原始回溯:\n\n%s\n”
--->16(消息,回溯字符串))
类型错误:a=1,b=1j
原始回溯:
回溯(最近一次呼叫最后一次):
文件“”,第7行,在
m=max(a,b)#无法将int与复数进行比较
TypeError:没有为复数定义排序关系


我知道这与问题中提供的示例有点不同,但我希望有人觉得它有用

如果您来这里搜索Python 3的解决方案,则会说:

当引发新异常时(而不是使用裸的
raise
重新引发当前正在处理的异常),可以通过使用
from
raise
以明确原因补充隐式异常上下文:


例如:

try:
    return [permission() for permission in self.permission_classes]
except TypeError as e:
    raise TypeError("Make sure your view's 'permission_classes' are iterable. "
                    "If you use '()' to generate a set with a single element "
                    "make sure that there is a comma behind the one (element,).") from e
最后看起来是这样的:

2017-09-06 16:50:14797[错误]django.request:内部服务器错误:/v1/sendmail/
回溯(最近一次呼叫最后一次):
文件“venv/lib/python3.4/site packages/rest\u framework/views.py”,第275行,在get\u权限中
返回self.pe中权限的[permission()
raise new_exc from original_exc
try:
    return [permission() for permission in self.permission_classes]
except TypeError as e:
    raise TypeError("Make sure your view's 'permission_classes' are iterable. "
                    "If you use '()' to generate a set with a single element "
                    "make sure that there is a comma behind the one (element,).") from e
except Exception as e:
    e.args = ("Some failure state", *e.args)
    raise
foo = None

try:
    try:
        state = "bar"
        foo.append(state)

    except Exception as e:
        e.args = ("Appending '"+state+"' failed", *e.args)
        raise

    print(foo[0]) # would raise too

except Exception as e:
    e.args = ("print(foo) failed: " + str(foo), *e.args)
    raise
Traceback (most recent call last):
  File "test.py", line 6, in <module>
    foo.append(state)
AttributeError: ('print(foo) failed: None', "Appending 'bar' failed", "'NoneType' object has no attribute 'append'")
print("\n".join( "-"*i+" "+j for i,j in enumerate(e.args)))