PEP 597 – 添加可选的 EncodingWarning
- 作者:
- Inada Naoki <songofacandy at gmail.com>
- 状态:
- 最终
- 类型:
- 标准跟踪
- 创建:
- 2019-06-05
- Python 版本:
- 3.10
摘要
添加一个新的警告类别 EncodingWarning
。当 open()
的 encoding
参数被省略,并且使用默认的特定于区域设置的编码时,会发出此警告。
默认情况下,警告被禁用。可以使用新的 -X warn_default_encoding
命令行选项和新的 PYTHONWARNDEFAULTENCODING
环境变量来启用它。
还添加了 "locale"
作为 encoding
的参数值。它显式地指定应使用区域设置编码,从而抑制警告。
动机
使用默认编码是一个常见的错误
使用 macOS 或 Linux 的开发人员可能忘记默认编码并不总是 UTF-8。
例如,在 setup.py
中使用 long_description = open("README.md").read()
是一个常见的错误。如果他们的 UTF-8 编码的 README.md
文件中至少有一个非 ASCII 字符(例如表情符号、作者姓名、版权符号等),许多 Windows 用户将无法安装此类包。
在 PyPI 上下载次数最多的 4000 个包中,有 489 个包在其 README 中使用非 ASCII 字符,并且有 82 个包由于未为非 ASCII 文件指定编码而无法从源代码安装到非 UTF-8 区域设置中。 [1]
另一个例子是 logging.basicConfig(filename="log.txt")
。一些用户可能期望它默认使用 UTF-8,但实际上使用的是区域设置编码。 [2]
即使是 Python 专家也可能认为默认编码是 UTF-8。这会导致仅在 Windows 上发生的错误;例如,请参见 [3]、[4]、[5] 和 [6]。
当省略 encoding
参数时发出警告将有助于发现此类错误。
使用特定于区域设置的编码的显式方法
open(filename)
没有明确说明预期使用哪种编码
- 如果假设 ASCII,这不算错误,但可能会导致 Windows 上的性能下降,尤其是对于非 Latin-1 区域设置编码
- 如果假设 UTF-8,这可能是一个错误或一个特定于平台的脚本
- 如果假设区域设置编码,则行为符合预期(但如果 Python 的未来版本修改了默认值,则可能会发生变化)
从这个角度来看,open(filename)
不是可读的代码。
encoding=locale.getpreferredencoding(False)
可用于显式地指定区域设置编码,但它太长且易于误用(例如,人们可能忘记将 False
作为其参数传递)。
本 PEP 提供了一种显式的方法来指定区域设置编码。
准备将默认编码更改为 UTF-8
由于 UTF-8 已成为事实上的标准文本编码,我们将来可能会将其作为打开文件的默认编码。
但是,这种更改将影响许多应用程序和库。如果我们开始在所有省略 encoding
参数的地方发出 DeprecationWarning
,它将过于嘈杂和痛苦。
虽然本 PEP 并未提出更改默认编码,但它将有助于实现此更改,方法是
- 在我们开始默认发出
DeprecationWarning
之前,减少库中省略的encoding
参数的数量。 - 允许用户传递
encoding="locale"
来抑制当前警告和将来添加的任何DeprecationWarning
,以及在以后的 Python 版本更改默认值时保留一致的行为,确保对任何 Python 版本 >=3.10 的支持。
规范
EncodingWarning
添加一个新的 EncodingWarning
警告类,它是 Warning
的子类。当省略 encoding
参数并且使用默认的特定于区域设置的编码时,会发出此警告。
启用警告的选项
添加了 -X warn_default_encoding
选项和 PYTHONWARNDEFAULTENCODING
环境变量。它们用于启用 EncodingWarning
。
sys.flags.warn_default_encoding
也被添加。当 EncodingWarning
被启用时,该标志为真。
当设置此标志时,io.TextIOWrapper()
、open()
和其他使用它们的模块将在省略 encoding
参数时发出 EncodingWarning
。
由于 EncodingWarning
是 Warning
的子类,因此它们默认情况下会被显示(如果设置了 warn_default_encoding
标志),这与 DeprecationWarning
不同。
encoding="locale"
io.TextIOWrapper
将接受 "locale"
作为 encoding
的有效参数。它与当前的 encoding=None
具有相同的含义,只是 io.TextIOWrapper
在指定 encoding="locale"
时不会发出 EncodingWarning
。
io.text_encoding()
io.text_encoding()
是一个辅助函数,用于具有 encoding=None
参数的函数,这些函数将其传递给 io.TextIOWrapper()
或 open()
。
纯 Python 实现将如下所示
def text_encoding(encoding, stacklevel=1):
"""A helper function to choose the text encoding.
When *encoding* is not None, just return it.
Otherwise, return the default text encoding (i.e. "locale").
This function emits an EncodingWarning if *encoding* is None and
sys.flags.warn_default_encoding is true.
This function can be used in APIs with an encoding=None parameter
that pass it to TextIOWrapper or open.
However, please consider using encoding="utf-8" for new APIs.
"""
if encoding is None:
if sys.flags.warn_default_encoding:
import warnings
warnings.warn(
"'encoding' argument not specified.",
EncodingWarning, stacklevel + 2)
encoding = "locale"
return encoding
例如,pathlib.Path.read_text()
可以像这样使用它
def read_text(self, encoding=None, errors=None):
encoding = io.text_encoding(encoding)
with self.open(mode='r', encoding=encoding, errors=errors) as f:
return f.read()
通过使用 io.text_encoding()
,EncodingWarning
将针对 read_text()
的调用者发出,而不是针对 read_text()
本身发出。
受影响的标准库模块
许多标准库模块将受到此更改的影响。
大多数接受 encoding=None
的 API 将使用上一节中所述的 io.text_encoding()
。
在使用区域设置编码作为默认编码合理的情况下,将使用 encoding="locale"
。例如,subprocess
模块将使用区域设置编码作为管道默认编码。
许多测试使用 open()
而不指定 encoding
来读取 ASCII 文本文件。它们应该用 encoding="ascii"
重写。
基本原理
选择性警告
虽然 DeprecationWarning
默认情况下被抑制,但当省略 encoding
参数时总是发出 DeprecationWarning
会过于嘈杂。
嘈杂的警告可能会导致开发人员忽略 DeprecationWarning
。
“locale”不是一个编解码器别名
我们没有将 “locale” 添加为编解码器别名,因为区域设置可以在运行时更改。
此外,TextIOWrapper
在 encoding=None
时检查 os.device_encoding()
。这种行为无法在编解码器中实现。
向后兼容性
新的警告默认情况下不会发出,因此本 PEP 与向后兼容。
向前兼容性
将 "locale"
作为参数传递给 encoding
并非向前兼容。使用它的代码在 Python 3.10 之前的版本中将无法工作,并且会引发 LookupError: unknown encoding: locale
。
在开发人员能够放弃 Python 3.9 支持之前,EncodingWarning
只能用于查找缺少的 encoding="utf-8"
参数。
如何教授
对于新用户
由于 EncodingWarning
用于编写跨平台代码,因此无需向新用户教授它。
我们只需推荐使用 UTF-8 作为文本文件,并在打开它们时使用 encoding="utf-8"
。
对于有经验的用户
使用 open(filename)
读取以 UTF-8 编码的文本文件是一个常见的错误。它可能无法在 Windows 上工作,因为 UTF-8 不是默认编码。
您可以使用 -X warn_default_encoding
或 PYTHONWARNDEFAULTENCODING=1
来查找此类错误。
在打开以区域设置编码的文本文件时省略 encoding
参数并非错误,但在 Python 3.10 及更高版本中,建议使用 encoding="locale"
,因为它更明确。
参考实现
讨论
最新讨论主题是:https://mail.python.org/archives/list/python-dev@python.org/thread/SFYUP2TWD5JZ5KDLVSTZ44GWKVY4YNCV/
参考文献
版权
本文件放置在公共领域或根据 CC0-1.0-Universal 许可证,以较宽松的许可为准。
来源:https://github.com/python/peps/blob/main/peps/pep-0597.rst
最后修改时间:2023-09-09 17:39:29 GMT