Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 增强提案

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 提出了一个方案。

基本原理

有两种基本方法可以生成异常

  1. Python 自己生成(代码错误、资源不足、循环结束等)
  2. 手动生成(使用 raise 语句)

在编写库,甚至只是自定义类时,可能需要引发异常;此外,从一种异常更改为另一种异常可能很有用,甚至必要。举一个来自我的 dbf 模块的例子

try:
    value = int(value)
except Exception:
    raise DbfError(...)

原始异常是什么(ValueErrorTypeError 或其他)无关紧要。从这一点开始的异常是 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__

    被拒绝,因为它可能令人困惑,可能被用户错误地引发,并且不是真正唯一的值,因为 NoneTrueFalse 是。

  • 使用 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

上次修改:2023年9月9日 17:39:29 GMT