如何在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)
167277 次浏览
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'

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

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


将一个消息附加到当前异常并重新引发它: (外部的try/except只是为了显示效果)

对于python 2。其中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)

这也将做正确的事情if err衍生ValueError。例如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))

Edit2:

根据目的,您还可以选择在自己的变量名下添加额外的信息。对于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

这个代码模板应该允许您用自定义消息引发异常。

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

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

## 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。X,这真的变成了一件美丽的事情:)

提高从

我们可以使用提高从链接异常。

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

注意,底部的异常中有执行无效除法的行,也有重新引发异常的行。

当前的答案对我来说并不是很好,如果没有重新捕获异常,则不会显示附加的消息。

但是像下面这样做既保持跟踪,又显示附加的消息,不管是否重新捕获异常。

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

Python 3内置异常有strerror字段:

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

使用的错误消息引发新的异常

raise Exception('your error message')

raise ValueError('your error message')

在你想要引发它或使用'from'将错误消息附加(替换)到当前异常的地方(Python 3。只支持X):

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

上面的解决方案都没有完全满足我的要求,即在错误消息的第一部分添加一些信息,即我希望我的用户首先看到我的自定义消息。

这招对我很管用:

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)

这只适用于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')

试试下面:

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:
import settings
except ModuleNotFoundError:
print("Something meaningfull\n")
raise

因此,您将首先打印错误消息,然后引发堆栈跟踪,或者您可以简单地通过sys.exit(1)退出,而根本不显示错误消息。

我尝试了这个精简版的@RobinL,效果很好:

try:
do_something_that_might_raise_an_exception()
except ValueError as e:
raise ValueError(f'Custom text {e}')