PEP 632 – 弃用 distutils 模块
- 作者:
- Steve Dower <steve.dower at python.org>
- 讨论列表:
- Discourse 帖子
- 状态:
- 最终
- 类型:
- 标准跟踪
- 创建:
- 2020年9月3日
- Python 版本:
- 3.10
- 历史记录:
- 2020年9月3日,2021年1月22日
- 决议:
- Python-Dev 帖子
摘要
distutils 模块 [1] 很长时间以来一直建议使用 setuptools 包 [2]。Setuptools 最近集成了 distutils 的完整副本,并且不再依赖于标准库 [3]。Pip 很久以来就在安装包时默默地用 setuptools 替换 distutils,并且 distutils 文档自 2014 年(或更早)以来就声明它正在逐步淘汰。现在是时候将其从标准库中删除了。
动机
distutils [1] 是一个很大程度上未记录且未维护的实用程序集合,用于打包和分发 Python 包,包括编译原生扩展模块。它定义了一种描述 Python 分发的配置格式,并提供将源代码目录转换为源分发版以及某些形式的二进制分发版的工具。由于它位于标准库中,因此许多更新只能随主要版本一起发布,用户无法依赖特定修复程序的可用性。
setuptools [2] 是一个记录良好且维护良好的基于 distutils 的增强功能。虽然它提供了非常类似的功能,但它更能够支持早期 Python 版本的用户,并且可以更快地响应错误报告。setuptools 中已经存在许多特定于平台的增强功能,这些增强功能尚未添加到 distutils 中,并且 distutils 文档中长期以来一直建议优先使用 setuptools。
从历史上看,setuptools 使用子类化和猴子补丁扩展了 distutils,但现在已经复制了底层代码。 [3] 因此,对 distutils 的倒数第二个主要依赖关系消失了,并且无需将其保留在标准库中。
对 distutils 的最终依赖关系是 CPython 本身,它使用 distutils 构建标准库中的原生扩展模块(Windows 除外)。因为这是一个 CPython 构建时依赖关系,所以可以在不将其作为标准库的一部分的情况下,继续在这种特定情况下使用 distutils。
弃用和删除将使问题应该在 setuptools 项目中修复变得显而易见,并将减少错误报告和不必要的测试维护来源。它还有助于促进替代构建后端的开发,由于 PEP 517,现在可以更容易地支持这些后端。
规范
在 Python 3.10 和 3.11 中,distutils 将被正式标记为已弃用。此时所有已知问题都将被关闭。 import distutils
将引发弃用警告。仍然可能会修复会被视为发布阻塞的新问题,但不会添加对新工具或平台的支持。
在 Python 3.10 和 3.11 期间,标准库中 distutils 的用法可能会更改为使用替代 API。
在 Python 3.12 中,make install
或任何第一方分发版将不再安装 distutils。第三方重新分发者不应在其捆绑包或存储库中包含 distutils。
本 PEP 并未对迁移当前使用 distutils 的 CPython 构建过程的部分做出任何规范。根据贡献,此迁移可能随时发生。
在 Python 3.12 启动后,并且当 CPython 构建过程不再依赖于标准库中的 distutils 时,整个 Lib/distutils
目录和 Lib/test/test_distutils.py
文件将从存储库中删除。
其他对 distutils 的引用将被清理。截至 Python 3.9 的初始版本,以下模块在代码或注释中包含引用
- Lib/ctypes/util.py
- Lib/site.py
- Lib/sysconfig.py
- Lib/_aix_support.py
- Lib/_bootsubprocess.py
- Lib/_osx_support.py
- Modules/_decimal/tests/formathelper.py
CPython 中的以下工具也引用了 distutils。请注意,这些工具都没有与 CPython 一起安装
- PC/layout(引用将被删除)
- Tools/msi(引用将被删除)
- Tools/peg_generator(将适应不同的构建工具)
- Tools/test2to3(示例项目将被删除)
由于 distutils 代码已包含在 setuptools 中,因此无需以任何其他形式重新发布它。需要访问该功能的人员应使用 setuptools 或其他构建后端。
向后兼容性
从 Python 3.12 开始,导入 distutils 的代码将不再有效。
建议的迁移路径是使用 setuptools 中等效(尽管不完全相同)的导入(参见 [5]),或迁移到替代构建后端(参见 PEP 517)。
setuptools 中已经存在代码可以透明地切换使用 distutils 的 setup.py
文件到其等效项,因此大多数有效的构建脚本已知可以与 setuptools 一起使用。此类脚本可能需要更新其导入语句。查阅 setuptools 文档以获取具体的迁移建议。 [5]
一些项目使用 distutils 上的替代补丁集,特别是 numpy.distutils。 [6] 我们知道正在这样做的项目已经收到了通知。
许多构建脚本使用自定义命令或范围狭窄的补丁。由于这些包已经受 setuptools 覆盖 distutils 的影响,因此我们预计 distutils 被删除后会造成最小的干扰。脚本可能仍然需要更新以避免导入 distutils。
参考实现
setuptools 版本 48 包含 distutils 的完整副本,因此不再依赖于标准库的副本。他们面临的大多数实现问题都是由于标准库中 distutils 的持续存在造成的,因此删除将提高其实现的稳定性。
尚未为从标准库中删除 distutils 提供参考实现,也没有为 CPython 的原生模块构建提供不依赖于标准库 distutils 副本的实现。
迁移建议
注意
本节建议了一些替代方案,用于替换本 PEP 正式弃用的常用功能。它在撰写时是当前的,但不会保持最新。
对于这些模块或类型,setuptools
是最佳替代品
distutils.ccompiler
distutils.cmd.Command
distutils.command
distutils.config
distutils.core.Distribution
distutils.errors
对于这些模块或类型,请使用指定的标准定义的 Python 包裹管理机构包
distutils.version
— 使用packaging
包
对于这些模块或函数,请使用所示的标准库模块
distutils.fancy_getopt
— 使用argparse
模块distutils.spawn.find_executable
— 使用shutil.which
函数distutils.spawn.spawn
— 使用subprocess.run
函数distutils.sysconfig
— 使用sysconfig
模块distutils.util.get_platform
— 使用platform
模块
对于这些函数以及此处未提及的任何其他函数,您需要自己重新实现该功能。可以在 https://docs.pythonlang.cn/3.9/distutils/apiref.html 找到旧版文档
distutils.dir_util.create_tree
distutils.util.change_root
distutils.util.strtobool
被拒绝的想法
弃用但不删除
这种方法的主要问题是 distutils 最常由于平台差异而出现故障,这意味着在没有维护的情况下,它将停止与任何 Python 版本同步工作。这使得库无法可靠地检测何时停止工作。
相反,本 PEP 提出一个具体日期,提前很久就知道,届时 distutils 将停止工作,并承诺在此之前不会破坏 API。这为维护人员提供了一个可预测的时间表,确保任何中断都发生在用户已经预期行为发生变化的时刻,并提供了一种可靠的检测机制(具体来说,import distutils
会引发异常)。
最后,只要 distutils 以任何形式保留在标准库中,它就会干扰提供垫片或替换的第三方包,包括 setuptools。在已知版本中完全删除该包使第三方能够安全地使用替代品。
仅弃用类似 setuptools 的功能
此建议假设存在志愿者来维护剩余的内容,但事实并非如此。它还暗示每个人都知道哪些功能应该保留,正如讨论中所看到的,这一点根本不清楚。
distutils 中的大多数辅助函数已经有了支持的(并且改进的)替代方案,通常在标准库中,并且在不破坏兼容性的情况下对旧版进行的操作很少。(并且任何需要维护人员更新其代码的中断本质上等同于要求他们导入不同的函数。)
上一节的最后一点也适用于此。
参考文献
版权
本文件置于公有领域或根据 CC0-1.0-通用许可证,以两者中较宽松者为准。
来源:https://github.com/python/peps/blob/main/peps/pep-0632.rst