如何在 Python 中使用自定义消息引发相同的异常



我的代码中有这个try块:

try:
    do_something_that_might_raise_an_exception()
except ValueError as err:
    errmsg = 'My custom error message.'
    raise ValueError(errmsg)

严格来说,我实际上是在提出另一个ValueError,而不是do_something...()抛出的ValueError,在这种情况下被称为err。如何将自定义消息附加到err?我尝试以下代码,但由于err,一个ValueError实例,不可调用而失败:

try:
    do_something_that_might_raise_an_exception()
except ValueError as err:
    errmsg = 'My custom error message.'
    raise err(errmsg)
如果你

足够幸运,只支持python 3.x,这真的变成了一件美丽的事情:)

我们可以使用 raise from 链接异常。

try:
    1 / 0
except ZeroDivisionError as e:
    raise Exception('Smelly socks') from e

在这种情况下,调用方将捕获的异常具有我们引发异常的位置的行号。

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "test.py", line 4, in <module>
    raise Exception('Smelly socks') from e
Exception: Smelly socks

请注意,底部异常仅包含我们引发异常的堆栈跟踪。调用方仍可以通过访问他们捕获的异常的 __cause__ 属性来获取原始异常。

with_traceback

或者您可以使用with_traceback。

try:
    1 / 0
except ZeroDivisionError as e:
    raise Exception('Smelly socks').with_traceback(e.__traceback__)

使用此表单,调用方将捕获的异常具有发生原始错误的位置的回溯。

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "test.py", line 4, in <module>
    raise Exception('Smelly socks').with_traceback(e.__traceback__)
  File "test.py", line 2, in <module>
    1 / 0
Exception: Smelly socks

请注意,底部异常包含我们执行无效除法的行以及我们重新引发异常的行。

更新:对于 Python 3,请检查 Ben 的答案

2023 年更新:我十多年前写过这个答案,现在有更好的答案。你应该使用python 3和上面的答案。

原答案:

要将消息附加到当前异常并重新引发它,请执行以下操作:(外试/除外只是为了显示效果(

对于 python 2.x,其中 x>=6:

try:
    try:
      raise ValueError  # something bad...
    except ValueError as err:
      err.message=err.message+" hello"
      raise              # re-raise current exception
except ValueError as e:
    print(" got error of type "+ str(type(e))+" with message " +e.message)

如果errValueError,这也将做正确的事情。例如UnicodeDecodeError .

请注意,您可以添加任何您喜欢的内容err。例如err.problematic_array=[1,2,3] .

<小时 />

编辑:在评论中@Ducan点,上述内容不适用于python 3,因为.message不是ValueError的成员。相反,您可以使用这个(有效的python 2.6或更高版本或3.x(:

try:
    try:
      raise ValueError
    except ValueError as err:
       if not err.args: 
           err.args=('',)
       err.args = err.args + ("hello",)
       raise 
except ValueError as e:
    print(" error was "+ str(type(e))+str(e.args))

编辑2:

根据目的,您还可以选择在自己的变量名称下添加额外的信息。对于 python2 和 python3:

try:
    try:
      raise ValueError
    except ValueError as err:
       err.extra_info = "hello"
       raise 
except ValueError as e:
    print(" error was "+ str(type(e))+str(e))
    if 'extra_info' in dir(e):
       print e.extra_info

这仅适用于Python 3。 您可以修改异常的原始参数并添加自己的参数。

异常会记住创建它的参数。 我认为这是为了您可以修改异常。

在函数reraise中,我们在异常的原始参数前面加上我们想要的任何新参数(如消息(。 最后,我们在保留回溯历史记录的同时重新引发异常。

def reraise(e, *args):
  '''re-raise an exception with extra arguments
  :param e: The exception to reraise
  :param args: Extra args to add to the exception
  '''
  # e.args is a tuple of arguments that the exception with instantiated with.
  #
  e.args = args + e.args
  # Recreate the exception and preserve the traceback info so that we can see 
  # where this exception originated.
  #
  raise e.with_traceback(e.__traceback__)   

def bad():
  raise ValueError('bad')
def very():
  try:
    bad()
  except Exception as e:
    reraise(e, 'very')
def very_very():
  try:
    very()
  except Exception as e:
    reraise(e, 'very')
very_very()

输出

Traceback (most recent call last):
  File "main.py", line 35, in <module>
    very_very()
  File "main.py", line 30, in very_very
    reraise(e, 'very')
  File "main.py", line 15, in reraise
    raise e.with_traceback(e.__traceback__)
  File "main.py", line 28, in very_very
    very()
  File "main.py", line 24, in very
    reraise(e, 'very')
  File "main.py", line 15, in reraise
    raise e.with_traceback(e.__traceback__)
  File "main.py", line 22, in very
    bad()
  File "main.py", line 18, in bad
    raise ValueError('bad')
ValueError: ('very', 'very', 'bad')

似乎所有的答案都在向 e.args[0] 添加信息,从而改变现有的错误消息。 扩展参数元组是否有缺点? 我认为可能的好处是,对于需要解析该字符串的情况,您可以保留原始错误消息;如果自定义错误处理生成了多个消息或错误代码,则可以向元组添加多个元素,以便在以编程方式(例如通过系统监视工具(解析回溯的情况下。

## Approach #1, if the exception may not be derived from Exception and well-behaved:
def to_int(x):
    try:
        return int(x)
    except Exception as e:
        e.args = (e.args if e.args else tuple()) + ('Custom message',)
        raise
>>> to_int('12')
12
>>> to_int('12 monkeys')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in to_int
ValueError: ("invalid literal for int() with base 10: '12 monkeys'", 'Custom message')

## Approach #2, if the exception is always derived from Exception and well-behaved:
def to_int(x):
    try:
        return int(x)
    except Exception as e:
        e.args += ('Custom message',)
        raise
>>> to_int('12')
12
>>> to_int('12 monkeys')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in to_int
ValueError: ("invalid literal for int() with base 10: '12 monkeys'", 'Custom message')

你能看到这种方法的缺点吗?

Python 3.11+

PEP 678 – Enriching Exceptions with Notes 被接受并登陆 Python 3.11。新的 API 允许用户将自定义消息附加到现有错误。这对于在遇到错误时添加其他上下文非常有用。

使用add_note方法适合回答原始问题:

try:
    int("eleven")
except ValueError as e:
    errmsg = "My custom error message."
    e.add_note(errmsg)
    raise

它将呈现如下:

Traceback (most recent call last):
  File "/tmp/example.py", line 2, in <module>
    int("eleven")
ValueError: invalid literal for int() with base 10: 'eleven'
My custom error message.

Python <3.11

修改 BaseException.__str__ 用来呈现异常的 args 属性是唯一的方法。您可以扩展参数:

try:
    int("eleven")
except ValueError as e:
    errmsg = "My custom error message."
    e.args += (errmsg,)
    raise e

这将呈现为:

Traceback (most recent call last):
  File "/tmp/example.py", line 2, in <module>
    int("eleven")
ValueError: ("invalid literal for int() with base 10: 'eleven'", 'My custom error message.')

或者你可以替换args[0],这有点复杂,但会产生更干净的结果。

try:
    int("eleven")
except ValueError as e:
    errmsg = "My custom error message."
    args = e.args
    if not args:
        arg0 = errmsg
    else:
        arg0 = f"{args[0]}n{errmsg}"
    e.args = (arg0,) + args[1:]
    raise

这将以与 Python 3.11+ 异常相同的方式呈现__notes__

Traceback (most recent call last):
  File "/tmp/example.py", line 2, in <module>
    int("eleven")
ValueError: invalid literal for int() with base 10: 'eleven'
My custom error message.
try:
    try:
        int('a')
    except ValueError as e:
        raise ValueError('There is a problem: {0}'.format(e))
except ValueError as err:
    print err

指纹:

There is a problem: invalid literal for int() with base 10: 'a'

此代码模板应允许您使用自定义消息引发异常。

try:
     raise ValueError
except ValueError as err:
    raise type(err)("my message")

这是我用来修改 Python 2.7 和 3.x 中的异常消息的函数,同时保留原始回溯。它需要six

def reraise_modify(caught_exc, append_msg, prepend=False):
    """Append message to exception while preserving attributes.
    Preserves exception class, and exception traceback.
    Note:
        This function needs to be called inside an except because
        `sys.exc_info()` requires the exception context.
    Args:
        caught_exc(Exception): The caught exception object
        append_msg(str): The message to append to the caught exception
        prepend(bool): If True prepend the message to args instead of appending
    Returns:
        None
    Side Effects:
        Re-raises the exception with the preserved data / trace but
        modified message
    """
    ExceptClass = type(caught_exc)
    # Keep old traceback
    traceback = sys.exc_info()[2]
    if not caught_exc.args:
        # If no args, create our own tuple
        arg_list = [append_msg]
    else:
        # Take the last arg
        # If it is a string
        # append your message.
        # Otherwise append it to the
        # arg list(Not as pretty)
        arg_list = list(caught_exc.args[:-1])
        last_arg = caught_exc.args[-1]
        if isinstance(last_arg, str):
            if prepend:
                arg_list.append(append_msg + last_arg)
            else:
                arg_list.append(last_arg + append_msg)
        else:
            arg_list += [last_arg, append_msg]
    caught_exc.args = tuple(arg_list)
    six.reraise(ExceptClass,
                caught_exc,
                traceback)

使用

raise Exception('your error message')

raise ValueError('your error message')

在您想要引发它的地方或使用"from"将错误消息附加(替换(到当前异常中(仅支持 Python 3.x(:

except ValueError as e:
  raise ValueError('your message') from e

试试下面:

try:
    raise ValueError("Original message. ")
except Exception as err:
    message = 'My custom error message. '
    # Change the order below to "(message + str(err),)" if custom message is needed first. 
    err.args = (str(err) + message,)
    raise 

输出:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
      1 try:
----> 2     raise ValueError("Original message")
      3 except Exception as err:
      4     message = 'My custom error message.'
      5     err.args = (str(err) + ". " + message,)
ValueError: Original message. My custom error message.

当前的答案对我不起作用,如果没有重新捕获异常,则不会显示附加的消息。

但是,在下面这样做既可以保留跟踪,也可以显示附加的消息,无论是否重新捕获异常。

try:
  raise ValueError("Original message")
except ValueError as err:
  t, v, tb = sys.exc_info()
  raise t, ValueError(err.message + " Appended Info"), tb

(我用的是Python 2.7,没有在Python 3中尝试过(

Python 3 内置异常具有strerror字段:

except ValueError as err:
  err.strerror = "New error message"
  raise err

上述解决方案都没有完全达到我想要的效果,即向错误消息的第一部分添加一些信息,即我希望我的用户首先看到我的自定义消息。

这对我有用:

exception_raised = False
try:
    do_something_that_might_raise_an_exception()
except ValueError as e:
    message = str(e)
    exception_raised = True
if exception_raised:
    message_to_prepend = "Custom text"
    raise ValueError(message_to_prepend + message)

我尝试了这个紧凑版本的@RobinL,并且也工作:

try:
    do_something_that_might_raise_an_exception()
except ValueError as e:
    error_message = 'My error message'
    raise ValueError(f'Custom text {error_message}')

引发相同的错误,前面有前缀自定义文本消息。(编辑 - 对不起,实际上和 https://stackoverflow.com/a/65494175/15229310 一样,为什么有 10 个(投票("解决方案"根本不回答发布的问题?

    try:
       <code causing exception>
    except Exception as e:
        e.args = (f"My custom text. Original Exception text: {'-'.join(e.args)}",)
        raise

如果要自定义错误类型,您可以做的一件简单的事情就是基于 ValueError 定义一个错误类。

最新更新