setuptools与distutils:为什么distutils仍然存在?

204 投票
4 回答
61547 浏览
提问于 2025-04-18 17:29

Python在打包和描述项目的工具上有一段复杂的历史,涉及到很多工具,比如标准库里的distutilsdistributedistutils2setuptools(可能还有其他的)。看起来distributedistutils2已经不再使用,取而代之的是setuptools,这就留下了两个竞争的标准。

根据我的理解,setuptools提供了比distutils更多的选项(比如声明依赖、测试等),但是它还不在Python的标准库里(还没?)。

Python打包用户指南[1]现在推荐:

使用setuptools来定义项目并创建源分发。

并且解释说:

虽然你可以用纯distutils来处理很多项目,但它不支持定义对其他项目的依赖,并且缺少一些setuptools提供的便利工具,这些工具可以自动正确填充包的元数据。由于setuptools不在标准库中,它在不同版本的Python中也提供了更一致的功能集,并且(与distutils不同),setuptools会更新以支持即将到来的“Metadata 2.0”标准格式。

即使是选择使用distutils的项目,当pip直接从源代码安装这些项目时(而不是从预构建的wheel文件安装),它实际上会使用setuptools来构建你的项目。

然而,查看各种项目的setup.py文件会发现,这似乎并不是一个真正的标准。许多包仍然使用distutils,而那些支持setuptools的包往往会将setuptoolsdistutils混合使用,比如通过回退导入的方式:

try:
    from setuptools import setup
except ImportError:
    from distutils.core import setup

接着尝试找到一种方法来编写一个可以被setuptoolsdistutils都安装的setup。这通常包括各种容易出错的依赖检查方式,因为distutils在setup函数中不支持依赖。

为什么人们还在额外努力支持distutils呢?难道setuptools不在标准库里就是唯一的原因吗?distutils有什么优势,而只支持setuptoolssetup.py文件又有什么缺点呢?

4 个回答

11

基本上,这是因为责任的划分。

setuptools 不是 Python 标准库的一部分,因为它是由第三方维护的,而不是 Python 核心团队。这意味着,除了其他方面:

  • 它不在核心测试套件中,也不依赖于核心功能。
  • 它本身并不设定附加模块的核心标准(比如它们的位置、导入方式、C 扩展的二进制接口等)。
  • 它的更新和发布是独立于 Python 的版本发布的。

实际上,核心团队缩小了 distutils 的范围,将“核心标准”和“最小必要编译”的部分留给自己 而把所有其他的内容(扩展编译器/包格式/其他支持) 留给第三方。之前覆盖这些“扩展部分”的代码被保留下来以保持向后兼容。

来自 分发 Python 模块 — Python 2.7.12 文档

虽然直接使用 distutils 正在逐步淘汰,但它仍然为当前的打包和分发基础设施奠定了基础,它不仅仍然是标准库的一部分,而且它的名字以其他方式继续存在(例如,用于协调 Python 打包标准开发的邮件列表的名称)。

其他操作系统的包也可能会单独提供 setuptoolspip - 这是因为上述原因

  • 而且当系统上已经有其他包管理器时,它们并不是必要的,甚至可能对可维护性有害。
12

虽然setuptools无疑是更好的工具,但我们仍然会提到和使用distutils,原因有几个。

首先,distutils几乎在所有地方都能找到。如果你想创建一个可以分享给别人的模块,而且没有什么复杂的要求,那么在你的工作机器上肯定能找到它。这一点在你需要支持旧版本的Python,或者在一个不熟悉的环境中工作时尤其重要。

其次,setuptools是在distutils的基础上进行改进的。它的结构和功能都是基于distutils的。setuptools的文档假设读者已经熟悉distutils,只讲述它是如何增强这个基础工具的。你可以把distutils想象成一种方言,而setuptools则是在这种方言上进行的提升。

我个人在新项目中的做法是,先假设我会使用distutils。只有当项目发展到需要setuptools的某个功能时,我才会进行升级。setuptools可以直接替代distutils,只需要在我的setup.py文件中做一行简单的修改。

19

setuptools不在标准库中是唯一的原因吗?

这只是一个原因。下面的内容直接来自于NumPy的setup.py

if len(sys.argv) >= 2 and ('--help' in sys.argv[1:] or
        sys.argv[1] in ('--help-commands', 'egg_info', '--version',
                        'clean')):
    # Use setuptools for these commands (they don't work well or at all
    # with distutils).  For normal builds use distutils.
    try:
        from setuptools import setup
    except ImportError:
        from distutils.core import setup

所以NumPy如果能找到setuptools就会优先使用它。但是SciPy以前也是这样做的,直到它被修改为在某些情况下优先使用distutils。在提交日志中提到:

setuptools会给测试脚本设置可执行权限,这样Nose就拒绝运行它们。最好不要这样做。

当然,setuptools和distribute的合并应该会在未来解决这些问题,但许多包仍然需要支持Python 2.6的安装。

107

看看这个StackOverflow的问题。它很好地解释了所有的打包方法,可能对你解决问题有帮助:distribute、distutils、setuptools和distutils2之间的区别?

Distutils 仍然是Python中打包的标准工具。它包含在标准库中(Python 2和Python 3.0到3.3)。它适用于简单的Python分发,但功能比较有限。它引入了一个叫做distutils的Python包,可以在你的setup.py脚本中导入。

Setuptools 是为了克服Distutils的局限性而开发的,它不包含在标准库中。它引入了一个命令行工具,叫做easy_install。它还引入了setuptools这个Python包,可以在你的setup.py脚本中导入,以及pkg_resources这个Python包,可以在你的代码中导入,用来查找与分发一起安装的数据文件。它的一个小问题是,它会对distutils这个Python包进行修改。它应该能很好地与pip配合使用。最新版本是在2013年7月发布的。

所以,正如你所看到的,setuptools应该优先于distutils。我理解你为什么会有这个问题,不过我认为distutils短期内不会失去支持,因为简单来说,它在很多流行的旧程序中仍然被使用。而且你可能知道,改变这些旧程序中的东西可能会非常麻烦,还会带来很多问题,比如不兼容,这会导致开发者不得不重写源代码。所以有这一点,还有distutils是标准Python库的一部分,而setuptools不是。所以,如果你现在在创建一个Python程序,建议使用setuptools,但要记住,没有distutils,setuptools也不会存在。

撰写回答