簡體   English   中英

如何在 Python 中使用自定義消息引發相同的異常?

[英]How do I raise the same Exception with a custom message in 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 的回答


將消息附加到當前異常並重新引發它:(外部 try/except 只是為了顯示效果)

對於 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

似乎所有的答案都在向 e.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')

你能看出這種方法的缺點嗎?

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 ValueError as err:
  t, v, tb = sys.exc_info()
  raise t, ValueError(err.message + " Appended Info"), tb

(我用的是 Python 2.7,沒有在 Python 3 中嘗試過)

這僅適用於 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 expection and preserve the traceback info so thta 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')

Python 3 內置異常具有strerror字段:

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

試試下面:

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.

我嘗試了@RobinL 的這個緊湊版本,並且效果很好:

try:
    do_something_that_might_raise_an_exception()
except ValueError as e:
    raise ValueError(f'Custom text {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)

上面提出的許多解決方案再次重新引發異常,這被認為是一種不好的做法。 像這樣簡單的事情就可以完成工作

try:
    import settings
except ModuleNotFoundError:
    print("Something meaningfull\n")
    raise 

因此,您將首先打印錯誤消息,然后引發堆棧跟蹤,或者您可以簡單地通過 sys.exit(1) 退出並且根本不顯示錯誤消息。

引發相同的錯誤,在前面添加自定義文本消息。 (編輯 - 抱歉,實際上與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 定義一個錯誤類。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM