Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 624 – 移除 Py_UNICODE 编码器 API

作者:
Inada Naoki <songofacandy at gmail.com>
状态:
最终
类型:
标准跟踪
创建:
2020-07-06
Python 版本:
3.11
历史记录:
2020-07-08

目录

摘要

本 PEP 建议在 Python 3.11 中移除已弃用的 Py_UNICODE 编码器 API。

  • PyUnicode_Encode()
  • PyUnicode_EncodeASCII()
  • PyUnicode_EncodeLatin1()
  • PyUnicode_EncodeUTF7()
  • PyUnicode_EncodeUTF8()
  • PyUnicode_EncodeUTF16()
  • PyUnicode_EncodeUTF32()
  • PyUnicode_EncodeUnicodeEscape()
  • PyUnicode_EncodeRawUnicodeEscape()
  • PyUnicode_EncodeCharmap()
  • PyUnicode_TranslateCharmap()
  • PyUnicode_EncodeDecimal()
  • PyUnicode_TransformDecimalToASCII()

注意

PEP 623 建议移除与 Py_UNICODE 相关的 Unicode 对象 API。另一方面,本 PEP 与 Unicode 对象无关。这些 PEP 被拆分是因为它们具有不同的动机,需要不同的讨论。

动机

总的来说,减少长期弃用且用户很少的 API 数量是一个好主意,因为它不仅可以提高 CPython 的可维护性,还可以帮助 API 用户和其他 Python 实现。

基本原理

自 Python 3.3 起已弃用

Py_UNICODE 及其使用的 API 自 Python 3.3 起已弃用。

效率低下

所有这些 API 都使用 PyUnicode_FromWideChar 实现。因此,当用户想要编码 Unicode 对象时,这些 API 的效率低下。

使用不广泛

在搜索前 4000 个 PyPI 包 [1] 时,只有 pyodbc 使用了这些 API。

  • PyUnicode_EncodeUTF8()
  • PyUnicode_EncodeUTF16()

pyodbc 使用这些 API 将 Unicode 对象编码为字节对象。因此,修复它很容易。 [2]

替代 API

存在替代 API,它们接受 PyObject *unicode 而不是 Py_UNICODE *。用户可以迁移到它们。

已弃用的 API 替代 API
PyUnicode_Encode() PyUnicode_AsEncodedString()
PyUnicode_EncodeASCII() PyUnicode_AsASCIIString() (1)
PyUnicode_EncodeLatin1() PyUnicode_AsLatin1String() (1)
PyUnicode_EncodeUTF7() (2)
PyUnicode_EncodeUTF8() PyUnicode_AsUTF8String() (1)
PyUnicode_EncodeUTF16() PyUnicode_AsUTF16String() (3)
PyUnicode_EncodeUTF32() PyUnicode_AsUTF32String() (3)
PyUnicode_EncodeUnicodeEscape() PyUnicode_AsUnicodeEscapeString()
PyUnicode_EncodeRawUnicodeEscape() PyUnicode_AsRawUnicodeEscapeString()
PyUnicode_EncodeCharmap() PyUnicode_AsCharmapString() (1)
PyUnicode_TranslateCharmap() PyUnicode_Translate()
PyUnicode_EncodeDecimal() (4)
PyUnicode_TransformDecimalToASCII() (4)

注释

  1. const char *errors 参数缺失。
  2. 没有公共的替代 API。但是用户可以使用通用的 PyUnicode_AsEncodedString() 替代。
  3. const char *errors, int byteorder 参数缺失。
  4. 没有直接的替代方案。但是可以使用 Py_UNICODE_TODECIMAL 替代。CPython 使用 _PyUnicode_TransformDecimalAndSpaceToASCII 将 Unicode 转换为数字。

计划

在 Python 3.11 中移除这些 API。它们已经弃用。

  • PyUnicode_Encode()
  • PyUnicode_EncodeASCII()
  • PyUnicode_EncodeLatin1()
  • PyUnicode_EncodeUTF7()
  • PyUnicode_EncodeUTF8()
  • PyUnicode_EncodeUTF16()
  • PyUnicode_EncodeUTF32()
  • PyUnicode_EncodeUnicodeEscape()
  • PyUnicode_EncodeRawUnicodeEscape()
  • PyUnicode_EncodeCharmap()
  • PyUnicode_TranslateCharmap()
  • PyUnicode_EncodeDecimal()
  • PyUnicode_TransformDecimalToASCII()

备选方案

PyObject* 替换 Py_UNICODE*

如“替代 API”部分所述,一些 API 没有接受 PyObject *unicode 输入的公共替代 API。一些公共替代 API 存在限制,例如缺少 errorsbyteorder 参数。

与其移除已弃用的 API,不如将它们重用为替代的公共 API。

由于我们已经拥有私有的替代 API,因此只需将私有名称重命名为公共名称和弃用名称即可。

重命名为 从以下重命名
PyUnicode_EncodeASCII() _PyUnicode_AsASCIIString()
PyUnicode_EncodeLatin1() _PyUnicode_AsLatin1String()
PyUnicode_EncodeUTF7() _PyUnicode_EncodeUTF7()
PyUnicode_EncodeUTF8() _PyUnicode_AsUTF8String()
PyUnicode_EncodeUTF16() _PyUnicode_EncodeUTF16()
PyUnicode_EncodeUTF32() _PyUnicode_EncodeUTF32()

优点

  • 我们拥有更一致的 API 集。

缺点

  • 向后不兼容。
  • 我们需要维护更多用于罕见用例的公共 API。
  • 现有的公共 API 足以满足大多数用例,在其他情况下可以使用 PyUnicode_AsEncodedString()

Py_UCS4* 替换 Py_UNICODE*

我们可以用 Py_UCS4 替换 Py_UNICODE,并将这些 API 恢复使用。

UTF-8、UTF-16、UTF-32 编码器在内部支持 Py_UCS4。因此,PyUnicode_EncodeUTF8()PyUnicode_EncodeUTF16()PyUnicode_EncodeUTF32() 可以避免创建临时 Unicode 对象。

优点

  • 使用 UTF-8、UTF-16、UTF-32 编解码器从 Py_UCS4* 编码为字节对象时,我们可以避免创建临时 Unicode 对象。

缺点

  • 向后不兼容。
  • 我们需要维护更多用于罕见用例的公共 API。
  • 其他想要支持 Python/C API 的 Python 实现也需要支持这些 API。
  • 如果我们将来将 Unicode 内部表示更改为 UTF-8,我们需要仅为这些 API 保留 UCS-4 支持。

wchar_t* 替换 Py_UNICODE*

我们可以用 wchar_t 替换 Py_UNICODE。由于 Py_UNICODE 已经是 wchar_t 的 typedef,因此这属于现状。

sizeof(wchar_t) == 4 的平台上,我们可以避免使用 UTF-8、UTF-16 和 UTF-32 编解码器从 wchar_t* 编码为字节对象时创建临时 Unicode 对象,就像“用 Py_UCS4* 替换 Py_UNICODE*”这个想法一样。

优点

  • 向后兼容。
  • sizeof(wchar_t) == 4 的平台上,我们可以避免使用 UTF-8、UTF-16 和 UTF-32 编解码器从 Py_UCS4* 编码为字节对象时创建临时 Unicode 对象。

缺点

  • 虽然 Windows 是最主要的、大量使用 wchar_t 的平台,但这些 API 始终需要创建临时 Unicode 对象,因为 Windows 上 sizeof(wchar_t) == 2
  • 我们需要维护更多用于罕见用例的公共 API。
  • 其他想要支持 Python/C API 的 Python 实现也需要支持这些 API。
  • 如果我们将来将 Unicode 内部表示更改为 UTF-8,我们需要仅为这些 API 保留 UCS-4 支持。

被拒绝的方案

发出运行时警告

除了现有的编译器警告外,还建议发出运行时 DeprecationWarning

但是,这些 API 现在没有释放 GIL。从这些 API 发出警告并不安全。请看这个例子。

PyObject *u = PyList_GET_ITEM(list, i);  // u is borrowed reference.
PyObject *b = PyUnicode_EncodeUTF8(PyUnicode_AS_UNICODE(u),
        PyUnicode_GET_SIZE(u), NULL);
// Assumes u is still living reference.
PyObject *t = PyTuple_Pack(2, u, b);
Py_DECREF(b);
return t;

如果我们从 PyUnicode_EncodeUTF8() 发出 Python 警告,警告过滤器和其他线程可能会更改 list,并且 uPyUnicode_EncodeUTF8() 返回后可能会成为悬空引用。

讨论

反对意见

  • 移除这些 API 会移除在没有临时 Unicode 的情况下使用编解码器的能力。
    • 自 Python 3.3 起,编解码器无法直接编码 Unicode 缓冲区,而无需临时 Unicode 对象。现在,所有这些 API 都会创建临时 Unicode 对象。因此,移除它们不会减少任何功能。
  • 为什么不也移除解码器 API 呢?
    • 它们是稳定的 ABI 的一部分。
    • PyUnicode_DecodeASCII()PyUnicode_DecodeUTF8() 被广泛使用。弃用它们得不偿失。
    • 解码器 API 可以直接从字节缓冲区解码,而无需创建临时字节对象。另一方面,编码器 API 无法避免临时 Unicode 对象。

参考文献


来源: https://github.com/python/peps/blob/main/peps/pep-0624.rst

最后修改时间: 2023-09-09 17:39:29 GMT