繁体   English   中英

Python "raise from" 用法

[英]Python "raise from" usage

raiseraise from in Python 有什么区别?

try:
    raise ValueError
except Exception as e:
    raise IndexError

哪个产量

Traceback (most recent call last):
  File "tmp.py", line 2, in <module>
    raise ValueError
ValueError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError
IndexError

try:
    raise ValueError
except Exception as e:
    raise IndexError from e

哪个产量

Traceback (most recent call last):
  File "tmp.py", line 2, in <module>
    raise ValueError
ValueError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError from e
IndexError

不同之处在于,当您使用from ,会设置__cause__属性,并且消息指出异常是由 直接引起的 如果省略from则不会设置__cause__ ,但也可以设置__context__属性,然后回溯显示上下文,如处理其他发生的事情

如果您在异常处理程序中使用raise ,则会设置__context__ 如果您在其他任何地方使用raise不会设置__context__

如果__cause__被设置,一个__suppress_context__ = True标志也被设置例外上; __suppress_context__设置为True时,打印回溯时会忽略__context__

从异常处理程序,你希望显示的背景下提出的时候,再使用(不处理另一个异常发生的消息希望) raise ... from None到组__suppress_context__True

换句话说,Python 为异常设置了一个上下文,以便您可以自省引发异常的位置,让您查看是否有另一个异常被它替换。 您还可以为异常添加原因,使回溯明确关于另一个异常(使用不同的措辞),并且上下文被忽略(但在调试时仍然可以自省)。 使用raise ... from None可以抑制正在打印的上下文。

请参阅raise语句文档

from子句用于异常链接:如果给定,第二个表达式必须是另一个异常类或实例,然后将作为__cause__属性(可写)附加到引发的异常。 如果未处理引发的异常,则将打印两个异常:

 >>> try: ... print(1 / 0) ... except Exception as exc: ... raise RuntimeError("Something bad happened") from exc ... Traceback (most recent call last): File "<stdin>", line 2, in <module> ZeroDivisionError: int division or modulo by zero The above exception was the direct cause of the following exception: Traceback (most recent call last): File "<stdin>", line 4, in <module> RuntimeError: Something bad happened

如果在异常处理程序或finally子句中引发异常,则类似的机制会隐式工作:然后将前一个异常附加为新异常的__context__属性:

 >>> try: ... print(1 / 0) ... except: ... raise RuntimeError("Something bad happened") ... Traceback (most recent call last): File "<stdin>", line 2, in <module> ZeroDivisionError: int division or modulo by zero During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<stdin>", line 4, in <module> RuntimeError: Something bad happened

另请参阅内置异常文档以获取有关上下文和附加到异常的原因信息的详细信息。

PEP 3134,Exception Chaining 和 Embedded Tracebacks引入了异常链接(通过显式raise EXCEPTION式引发进行隐链接,并raise EXCEPTION from CAUSE进行显式链接)。 以下是了解其用法的相关段落:

动机

在处理一个异常(异常A)的过程中,可能会发生另一个异常(异常B)。 在今天的Python(2.4版本)中,如果出现这种情况,异常B向外传播,异常A丢失。 为了调试问题,了解这两个异常是很有用的。 __context__属性自动保留此信息。

有时,异常处理程序有意重新引发异常以提供额外信息或将异常转换为另一种类型可能很有用。 __cause__属性提供了一种明确的方式来记录异常的直接原因。

[…]

隐式异常链接

下面是一个示例来说明__context__属性:

 def compute(a, b): try: a/b except Exception, exc: log(exc) def log(exc): file = open('logfile.txt') # oops, forgot the 'w' print >>file, exc file.close()

调用compute(0, 0)会导致ZeroDivisionError compute() function 捕获此异常并调用log(exc) ,但log() function 在尝试写入未打开进行写入的文件时也会引发异常。

在今天的 Python 中, compute()的调用者被抛出一个IOError ZeroDivisionError丢失了。 根据提议的更改, IOError的实例有一个额外的__context__属性,该属性保留ZeroDivisionError

[…]

显式异常链接

异常对象的__cause__属性总是初始化为None 它由一种新形式的raise语句设置:

 raise EXCEPTION from CAUSE

这相当于:

 exc = EXCEPTION exc.__cause__ = CAUSE raise exc

在下面的示例中,数据库提供了几种不同存储类型的实现,文件存储就是其中一种。 数据库设计者希望错误作为DatabaseError对象传播,这样客户端就不必知道特定于存储的详细信息,但又不想丢失底层错误信息。

 class DatabaseError(Exception): pass class FileDatabase(Database): def __init__(self, filename): try: self.file = open(filename) except IOError, exc: raise DatabaseError('failed to open') from exc

如果对open()的调用引发异常,该问题将被报告为DatabaseError ,并带有一个__cause__属性,该属性将IOError显示为原始原因。

增强报告

默认的异常处理程序将被修改为报告链式异常。 异常链通过遵循__cause____context__属性来遍历,其中__cause__优先。 为了与回溯的时间顺序保持一致,最近引发的异常显示在最后; 也就是说,显示从最内层异常的描述开始,然后将链备份到最外层异常。 回溯像往常一样格式化,其中一行:

上述异常是以下异常的直接原因:

要么

在处理上述异常的过程中,又出现了一个异常:

在回溯之间,取决于它们是否分别由__cause____context__链接。 这是程序的草图:

 def print_chain(exc): if exc.__cause__: print_chain(exc.__cause__) print '\nThe above exception was the direct cause...' elif exc.__context__: print_chain(exc.__context__) print '\nDuring handling of the above exception, ...' print_exc(exc)

[…]

PEP 415,使用异常属性实现上下文抑制然后引入了异常上下文的抑制( raise EXCEPTION from None )。 这是了解其用法的相关段落:

提议

__suppress_context__上的一个新属性BaseException将被引入。 每当设置__cause__时, __suppress_context__将设置为True 特别是, raise exc from cause语法会将exc.__suppress_context__设置为True 异常打印代码将检查该属性以确定是否打印上下文和原因。 __cause__将恢复到其最初的目的和价值。

__suppress_context__优先于print_line_and_file异常属性。

总而言之, raise exc from cause等同于:

 exc.__cause__ = cause raise exc

其中exc.__cause__ = cause隐式设置 exc exc.__suppress_context__

所以在 PEP 415 中,PEP 3134 中给出的过程的草图变成如下:

def print_chain(exc):
    if exc.__cause__:
        print_chain(exc.__cause__)
        print '\nThe above exception was the direct cause...'
    elif exc.__context__ and not exc.__suppress_context__:
        print_chain(exc.__context__)
        print '\nDuring handling of the above exception, ...'
    print_exc(exc)

最短的答案 PEP-3134说明了一切。 raise Exception from e设置新异常的__cause__

来自同一 PEP的更长答案

  • __context__字段将隐式设置为内部的原始错误except: block 除非被告知不要使用__suppress_context__ = True
  • __cause__就像上下文一样,但必须使用from语法显式设置
  • 当您在except块内调用raise时, traceback将始终链接。 您可以通过 a) 吞下异常except: pass或直接弄乱sys.exc_info()来摆脱回溯。

长答案

import traceback 
import sys

class CustomError(Exception):
    def __init__(self):
        super().__init__("custom")

def print_exception(func):
    print(f"\n\n\nEXECURTING FUNCTION '{func.__name__}' \n")
    try:
        func()
    except Exception as e:
        "Here is result of our actions:"
        print(f"\tException type:    '{type(e)}'")
        print(f"\tException message: '{e}'")
        print(f"\tException context: '{e.__context__}'")
        print(f"\tContext type:      '{type(e.__context__)}'")
        print(f"\tException cause:   '{e.__cause__}'")
        print(f"\tCause type:         '{type(e.__cause__)}'")
        print("\nTRACEBACKSTART>>>")
        traceback.print_exc()
        print("<<<TRACEBACKEND")


def original_error_emitter():
    x = {}
    print(x.does_not_exist)

def vanilla_catch_swallow():
    """Nothing is expected to happen"""
    try:
        original_error_emitter()
    except Exception as e:
        pass

def vanilla_catch_reraise():
    """Nothing is expected to happen"""
    try:
        original_error_emitter()
    except Exception as e:
        raise e

def catch_replace():
    """Nothing is expected to happen"""
    try:
        original_error_emitter()
    except Exception as e:
        raise CustomError()

def catch_replace_with_from():
    """Nothing is expected to happen"""
    try:
        original_error_emitter()
    except Exception as e:
        raise CustomError() from e

def catch_reset_trace():
    saw_an_error = False
    try:
        original_error_emitter()
    except Exception as e:
        saw_an_error = True
    if saw_an_error:
        raise CustomError()

print("Note: This will print nothing")
print_exception(vanilla_catch_swallow)
print("Note: This will print AttributeError and 1 stack trace")
print_exception(vanilla_catch_reraise)
print("Note: This will print CustomError with no context but 2 stack traces")
print_exception(catch_replace)
print("Note: This will print CustomError with AttributeError context and 2 stack traces")
print_exception(catch_replace_with_from)
print("Note: This will brake traceback chain")
print_exception(catch_reset_trace)

将导致以下 output:

Note: This will print nothing
EXECURTING FUNCTION 'vanilla_catch_swallow' 




Note: This will print AttributeError and 1 stack trace
EXECURTING FUNCTION 'vanilla_catch_reraise' 

        Exception type:    '<class 'AttributeError'>'
        Exception message: ''dict' object has no attribute 'does_not_exist''
        Exception context: 'None'
        Context type:      '<class 'NoneType'>'
        Exception cause:   'None'
        Cause type:         '<class 'NoneType'>'

TRACEBACKSTART>>>
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
    func()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 41, in vanilla_catch_reraise
    raise e
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 39, in vanilla_catch_reraise
    original_error_emitter()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
    print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'
<<<TRACEBACKEND



Note: This will print CustomError with no context but 2 stack traces
EXECURTING FUNCTION 'catch_replace' 

        Exception type:    '<class '__main__.CustomError'>'
        Exception message: 'custom'
        Exception context: ''dict' object has no attribute 'does_not_exist''
        Context type:      '<class 'AttributeError'>'
        Exception cause:   'None'
        Cause type:         '<class 'NoneType'>'

TRACEBACKSTART>>>
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 46, in catch_replace
    original_error_emitter()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
    print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
    func()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 48, in catch_replace
    raise CustomError()
CustomError: custom
<<<TRACEBACKEND



Note: This will print CustomError with AttributeError context and 2 stack traces
EXECURTING FUNCTION 'catch_replace_with_from' 

        Exception type:    '<class '__main__.CustomError'>'
        Exception message: 'custom'
        Exception context: ''dict' object has no attribute 'does_not_exist''
        Context type:      '<class 'AttributeError'>'
        Exception cause:   ''dict' object has no attribute 'does_not_exist''
        Cause type:         '<class 'AttributeError'>'

TRACEBACKSTART>>>
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 53, in catch_replace_with_from
    original_error_emitter()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
    print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
    func()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 55, in catch_replace_with_from
    raise CustomError() from e
CustomError: custom
<<<TRACEBACKEND



Note: This will brake traceback chain
EXECURTING FUNCTION 'catch_reset_trace' 

        Exception type:    '<class '__main__.CustomError'>'
        Exception message: 'custom'
        Exception context: 'None'
        Context type:      '<class 'NoneType'>'
        Exception cause:   'None'
        Cause type:         '<class 'NoneType'>'

TRACEBACKSTART>>>
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
    func()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 64, in catch_reset_trace
    raise CustomError()
CustomError: custom
<<<TRACEBACKEND

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM