我想达到这样的效果:

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')
Traceback...
  IOError('Stuff Happens at arg1')

但我得到的是:

Traceback..
  IOError('Stuff')

关于如何实现这一点,有什么线索吗?如何在Python 2和3中都做到这一点?


当前回答

假设你不想或不能修改foo(),你可以这样做:

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

这确实是在Python 3中解决问题的唯一解决方案,而不会出现丑陋而令人困惑的“在处理上述异常期间,发生了另一个异常”消息。

如果要将重新提升的行添加到堆栈跟踪中,则应该写入raise e而不是raise。

其他回答

我将提供一段我经常使用的代码片段,每当我想向异常添加额外信息时。我在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


我知道这与问题中提供的例子有一点偏差,但我希望有人觉得它有用。

假设你不想或不能修改foo(),你可以这样做:

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

这确实是在Python 3中解决问题的唯一解决方案,而不会出现丑陋而令人困惑的“在处理上述异常期间,发生了另一个异常”消息。

如果要将重新提升的行添加到堆栈跟踪中,则应该写入raise e而不是raise。

如果你来这里寻找Python 3的解决方案,手册上说:

当引发一个新的异常时(而不是使用一个简单的raise来重新引发当前正在处理的异常),隐式异常上下文可以通过使用from和raise来补充显式原因:

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

最后是这样的:

2017-09-06 16:50:14,797 [ERROR] django.request: Internal Server Error: /v1/sendEmail/
Traceback (most recent call last):
File "venv/lib/python3.4/site-packages/rest_framework/views.py", line 275, in get_permissions
    return [permission() for permission in self.permission_classes]
TypeError: 'type' object is not iterable 

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
    # Traceback removed...
TypeError: Make sure your view's Permission_classes are iterable. If 
     you use parens () to generate a set with a single element make 
     sure that there is a (comma,) behind the one element.

将一个完全无描述的TypeError转换为一个带有解决方案提示的漂亮消息,而不会搞乱原始异常。

以下是我在个人项目中使用的方法(我相信有足够的理由不在产品代码中这样做):

try:
    #something hazardous
except Exception as e:
    insightful_message = "shouldn't have done that"
    amended_args = tuple([f'{e.args[0]}\n{insightful_message}', *e.args[1:]])
    e.args = amended_args
    raise

代码(1)拦截错误;(2)创建错误的.args属性的副本,这是一个元组,假定在索引0处包含一个错误消息,使用列表理解实现;(3)在错误消息后追加换行符和自定义消息;(4)使用。args附加到副本的任何附加项 拆包;(5)将副本转换为元组;最后(6)用修改后的副本替换。args。

这些操作大多是为了避免.args元组的不可变性。

到目前为止,我对所有给出的答案都不满意。他们还是太啰嗦了。在代码和消息输出中。

所有我想要的是stacktrace指向源异常,没有异常之间的东西,所以没有创建新的异常,只是重新提升原始与所有相关的堆栈帧状态在它,导致那里。

史蒂夫·霍华德给出了一个很好的答案,我想扩展一下,不,是简化一下……仅适用于python 3。

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)))