我想达到这样的效果:

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中都做到这一点?


当前回答

也许

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

其他回答

我会这样做,这样在foo()中改变它的类型就不需要在bar()中也改变它。

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

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

bar('arg1')

Traceback (most recent call last):
  File "test.py", line 13, in <module>
    bar('arg1')
  File "test.py", line 11, in bar
    raise type(e)(e.message + ' happens at %s' % arg1)
IOError: Stuff happens at 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')

Traceback (most recent call last):
  File "test.py", line 16, in <module>
    bar('arg1')
  File "test.py", line 11, in bar
    foo()
  File "test.py", line 5, in foo
    raise IOError('Stuff')
IOError: Stuff happens at arg1

更新2

对于Python 3。在2012-05-16对PEP 352的修改中(我的第一次更新发布于2012-03-12),我的第一次更新中的代码在语法上是错误的,加上在BaseException上有消息属性的想法被撤销了。因此,目前在Python 3.5.2中,您需要按照这些行做一些事情来保留回溯,而不是在函数栏()中硬编码异常的类型。还要注意,会有这样一行:

During handling of the above exception, another exception occurred:

在显示的回溯消息中。

# 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

一位评论者询问是否有一种方法可以同时在Python 2和3中工作。尽管由于语法差异,答案似乎是“否”,但有一种解决方法,即使用six外接组件模块中的rerraise()等帮助函数。因此,如果您出于某种原因不想使用这个库,下面是一个简化的独立版本。

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

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

我使用的一个方便的方法是使用类属性作为详细信息的存储,因为类属性可以从类对象和类实例中访问:

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)

假设你不想或不能修改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。

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

例如:

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

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

也许

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