
分析:仍有数百个高下载量 PyPI 项目导入 Python 3.12/3.13 已移除模块
来源标题:PyPI code that breaks on Python 3.12 and 3.13
py-code.org 扫描 354,763 个 PyPI 项目发现,852 个高下载量项目仍无保护地导入 Python 3.12 和 3.13 已删除的标准库模块,运行时会直接报错。其中 544 个(63.8%)已两年未发布新版本,但每月仍被下载 1.26 亿次。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
2026年11月14日发布 · 数据截至2026年11月13日 本文的脚本和结果见GitHub Python 3.12从标准库中移除了distutils、imp、asyncore、asynchat和smtpd。Python 3.13又移除了20个模块,其中包括cgi、pipes、audioop和telnetlib。仍然导入这些模块的代码会报错停止,例如ModuleNotFoundError: No module named 'distutils'。 py-code.org读取了354,763个PyPI项目最新版本中的代码。我们列出了所有导入了这25个被移除模块中某一个的项目,然后结合上下文逐个读取每处导入,判断它是否真的会失败。位于try/except ImportError之中、或位于Python版本检查之后的导入是安全的;而文件顶部直接写一句import imp则不是。 结论:仍然会失败的导入集中在那些已经不再发布新版本的项目里。在852个下载量很大、且存在未加保护导入的项目中,有544个(63.8%)已经两年没有发布过新版本,而它们每月仍有1.26亿次下载。 Python 3.12移除了distutils(PEP 632),以及imp、asyncore、asynchat和smtpd模块。Python 3.13移除了lib2to3以及PEP 594中列出的19个“废弃电池”模块。Python 3.13的发布说明称,这些废弃电池模块被移除是“由于其历史性、过时或不安全的状况”。每个模块在被移除之前,都至少已经过两个版本的弃用期。 下表列出了每个模块、有多少个已收录项目导入了它、以及其中有多少个项目在自己的代码中(测试、setup脚本和文档之外)存在未加保护的导入。最后一列给出发布说明中提到的替代方案,或该模块在PyPI上的副本。 如何阅读这张表:“imports it”统计的是代码中在任何位置导入了该模块的所有项目,包括安全的、受保护的导入以及测试中的导入。“Unguarded”统计的是在自己的代码中至少有一处导入会失败的项目:位于文件顶部(该文件无法被导入),或位于某个函数内部(该函数运行时失败)。如果一个项目把PyPI上对应的副本(例如legacy-cgi)声明为必需依赖,则不计为未加保护。 distutils是被最多项目导入的模块:有2,565个项目导入它,其中973个存在未加保护的导入。但它也是唯一一个移除后错误往往不会显现的模块。Python 3.12的发布说明称“第三方包Setuptools继续提供distutils”。当安装了setuptools时,import distutils在3.12和3.13上仍然可用。 错误会在setuptools缺失时出现。从Python 3.12起,python -m venv不再在新创建的虚拟环境中安装setuptools,因此全新的venv、精简的Docker镜像或uv环境可能会遇到旧环境不会遇到的问题。以下这些包在导入时就会导入distutils,因此在3.12及更高版本上需要安装setuptools: 在1,623个存在未加保护导入的项目中,有890个项目的所有未加保护导入都是distutils,因此对它们来说pip install setuptools就够了。长久的解决办法是停止使用distutils:shutil.which可替代distutils.spawn.find_executable,packaging.version可替代distutils.version,sysconfig可替代distutils.sysconfig。 imp是过去手动加载模块的旧方式,已被importlib取代。默认情况下没有任何东西会把它装回来,因此未加保护的import imp在3.12及之后的每个Python版本上都会失败。有614个项目导入它,其中246个存在未加保护的导入。存在未加保护导入的项目中,有两个仍被广泛安装: 关键点:import nose和import boto在Python 3.12及更高版本上都会失败,但这两个包每月合计仍有520万次下载。 PyPI上的imp副本zombie-imp可以为无法修改的代码把这个模块重新带回来。对于你自己的代码,importlib.import_module、importlib.util.spec_from_file_location和importlib.reload可以覆盖imp过去的用途。 在3.13中移除的20个模块里,cgi(248个项目存在未加保护的导入)、imghdr(85个)和telnetlib(60个)是存在未加保护导入的项目数最多的。以下示例中,直接导入该包在3.13上会失败: pydub说明了为什么保护并不总是足够。它的pydub/utils.py这样保护导入: try: import audioop except ImportError: import pyaudioop as audioop 在3.13上,第一处导入失败,回退也失败,因为并不存在pyaudioop模块。于是import pydub会以No module named 'pyaudioop'停止。pydub每月有750万次下载,而它的最后一次发布是在2021年。安装audioop-lts(它提供一个audioop模块)可以让pydub重新工作。我们的统计把pydub的导入视为受保护的,因此pydub不在那1,623个之中。 提示:对于3.13移除的大多数模块,发布说明都给出了PyPI上的副本:cgi对应legacy-cgi或standard-cgi,audioop对应audioop-lts,其余模块对应standard-pipes、standard-imghdr、standard-telnetlib等。把它加入你自己的依赖需求,就能修复你无法修改的依赖。 在下载量最高的60,000个项目中,有2,202个导入了某个被移除的模块,其中852个在自己的代码中存在未加保护的导入。在我们人工阅读的仍在维护的项目中,许多这类导入位于不会在新版Python上运行的代码里。例如,pip默认使用sysconfig,只有当Python发行版配置它改用distutils时,它才会导入自己的distutils相关代码。有几个构建工具只在编译扩展的辅助代码中导入distutils。因此在这些仍在维护的项目中,未加保护的导入往往意味着某一个文件或某一个函数会失败,而包的其余部分仍然正常工作。 自2024年11月以来没有发布过新版本的项目,在这段时间内都没有发布修复。下面列出其中下载量最高的十个。与仍在维护的项目一样,未加保护的导入可能位于大多数用户从不会加载的部分。在future包中,未加保护的导入位于它自带的email模块副本中,以及它基于lib2to3构建的futurize和pasteurize转换工具中。import future本身是可以正常工作的。
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。