PEP 409 – 抑制异常上下文
- 作者:
- Ethan Furman <ethan at stoneleaf.us>
- 状态:
- 最终版
- 类型:
- 标准跟踪
- 创建日期:
- 2012 年 1 月 26 日
- Python 版本:
- 3.3
- 发布历史:
- 2002 年 8 月 30 日,2012 年 2 月 1 日,2012 年 2 月 3 日
- 取代者:
- 415
- 决议:
- Python-Dev 消息
摘要
PEP 3134 的一个开放问题是抑制上下文:目前没有办法做到这一点。本 PEP 提出了一个方案。
基本原理
生成异常有两种基本方式
- Python 自身(有 bug 的代码、缺少资源、结束循环等)
- 手动(使用 raise 语句)
在编写库,甚至只是自定义类时,可能需要抛出异常;此外,将一个异常更改为另一个异常可能很有用,甚至有必要。以我的 dbf 模块为例
try:
value = int(value)
except Exception:
raise DbfError(...)
无论原始异常是什么(ValueError、TypeError 或其他),都无关紧要。从这一点开始的异常是 DbfError,并且原始异常没有价值。但是,如果打印此异常,我们目前会看到两者。
备选方案
已经提出了几种可能性
raise as NewException()重新使用
as关键字;可能令人困惑,因为我们并未真正重新抛出原始异常raise NewException() from None遵循现有语法,明确声明原始异常
exc = NewException(); exc.__context__ = None; raise exc上一方法的非常冗长的方式
raise NewException.no_context(...)将上下文抑制设为类方法。
所有上述选项都需要对核心进行更改。
提案
我建议采用第二个选项
raise NewException from None
它的优点是使用现有的模式明确设置原因
raise KeyError() from NameError()
但由于原因是 None,因此默认异常打印例程不会显示以前的上下文。
实现讨论
注意:在此 PEP 获得批准后,在 PEP 415 中提出并接受了一个更简洁的实现机制。有关 Python 3.3 中实际使用的实现的更多详细信息,请参阅该 PEP。
目前,None 是 __context__ 和 __cause__ 的默认值。为了支持 raise ... from None(它会将 __cause__ 设置为 None),我们需要一个不同的 __cause__ 默认值。提出了几种关于如何在语言级别实现此目的的想法
- 覆盖以前的异常信息(绕过问题并将
__cause__保持为None)。由于错误消息不佳,这会严重阻碍调试,因此被拒绝。
- 在
__cause__中使用其中一个布尔值:False将是默认值,当from ...与显式链接的异常或None一起使用时,它将被替换。被拒绝,因为这鼓励为
__cause__使用两种不同的对象类型,其中一种(布尔值)不允许具有所有可能的完整值范围(True永远不会被使用)。 - 创建一个特殊的异常类
__NoException__。被拒绝,因为它可能令人困惑,可能被用户错误地引发,并且不是像
None、True和False那样真正独特的值。 - 使用
Ellipsis作为默认值(...单例)。已接受。
省略号在英语中常被用作省略单词时的占位符。这在这里对我们有利,作为
__cause__被省略的信号,因此请在__context__中查找更多详细信息。Ellipsis不是异常,因此不能被引发。只有一个
Ellipsis,因此没有未使用的值。错误信息不会被丢弃,因此即使默认代码不跟踪,自定义代码也可以跟踪整个异常链。
语言细节
为了支持 raise Exception from None,__context__ 将保持不变,但 __cause__ 将以 Ellipsis 开始,并在使用 raise Exception from None 方法时更改为 None。
| 形式 | __context__ | __cause__ |
|---|---|---|
| raise | 无 |
省略号 |
| reraise | 上一个异常 | 省略号 |
reraise from None | ChainedException |
上一个异常 | None | 显式链接的异常 |
默认的异常打印例程将
- 如果
__cause__是Ellipsis,则会打印__context__(如果有的话)。 - 如果
__cause__是None,则不会打印__context__。 - 如果
__cause__是其他任何东西,则会打印__cause__。
在后两种情况下,异常链将停止被跟踪。
因为 __cause__ 的默认值现在是 Ellipsis,并且 raise Exception from Cause 只是语法糖
_exc = NewException()
_exc.__cause__ = Cause()
raise _exc
现在,Ellipsis 和 None 都允许作为原因
raise Exception from Ellipsis
补丁
有一个 CPython 补丁实现了这一点,附加到 Issue 6210。
参考资料
python-dev 上的此线程中进行了讨论和改进。
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0409.rst