我想达到这样的效果:

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)

其他回答

这是我的实现,使用它作为上下文管理器,并可选地添加额外的消息异常:

from typing import Optional, Type
from types import TracebackType

class _addInfoOnException():
    def __init__(self, info: str = ""):
        self.info = info

    def __enter__(self):
        return

    def __exit__(self,
                 exc_type: Optional[Type[BaseException]],
                 exc_val: BaseException,  # Optional, but not None if exc_type is not None
                 exc_tb: TracebackType):  # Optional, but not None if exc_type is not None
        if exc_type:
            if self.info:
                newMsg = f"{self.info}\n\tLow level error: "
                if len(exc_val.args) == 0:
                    exc_val.args = (self.info, )
                elif len(exc_val.args) == 1:
                    exc_val.args = (f"{newMsg}{exc_val.args[0]}", )
                elif len(exc_val.args) > 0:
                    exc_val.args = (f"{newMsg}{exc_val.args[0]}", exc_val.args[1:])
            raise

用法:

def main():
    try:
        raise Exception("Example exception msg")
    except Exception:
        traceback.print_exc()
        print("\n\n")

    try:
        with _addInfoOnException():
            raise Exception("Example exception msg, no extra info")
    except Exception:
        traceback.print_exc()
        print("\n\n")

    try:
        with _addInfoOnException("Some extra info!"):
            raise Exception("Example exception msg")
    except Exception:
        traceback.print_exc()
        print("\n\n")


if __name__ == "__main__":
    main()

这将在这样的跟踪中解决:

Traceback (most recent call last):
  File "<...>\VSCodeDevWorkspace\testis.py", line 40, in main
    raise Exception("Example exception msg")
Exception: Example exception msg



Traceback (most recent call last):
  File "<...>\VSCodeDevWorkspace\testis.py", line 47, in main
    raise Exception("Example exception msg, no extra info")
  File "<...>\VSCodeDevWorkspace\testis.py", line 47, in main
    raise Exception("Example exception msg, no extra info")
Exception: Example exception msg, no extra info



Traceback (most recent call last):
  File "<...>\VSCodeDevWorkspace\testis.py", line 54, in main
    raise Exception("Example exception msg")
  File "<...>\VSCodeDevWorkspace\testis.py", line 54, in main
    raise Exception("Example exception msg")
Exception: Some extra info!
        Low level error: Example exception msg

在PEP 678中,本机支持向异常添加注释:

try:
  raise TypeError('bad type')
except Exception as e:
  e.add_note('Add some information')
  raise

呈现为:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: bad type
Add some information

我跳它可以取代史蒂夫霍华德的解决方案,不幸的是,它不给用户任何控制如何格式化最终的异常(例如,不能在异常之前添加一个注释,如:'错误在fn: {original_exc}')

如果想要对回溯进行更多控制,可以使用https://github.com/google/etils:

from etils import epy

with epy.maybe_reraise('Error in fn: '):
  fn()

Or:

try:
  fn()
except Exception as e:
  epy.reraise(e, suffix='. Did you mean y ?')

与之前的答案不同,这适用于非常糟糕的__str__异常。 但是,它确实修改了类型,以便剔除无用的__str__实现。

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

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!

也许

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

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

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元组的不可变性。