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 自己生成(代码错误、资源不足、循环结束等)
- 手动生成(使用 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__ |
---|---|---|
引发 | None |
Ellipsis |
重新引发 | 先前异常 | Ellipsis |
从 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