requirements.txt 对比 setup.py

requirements.txt vs setup.py

我开始使用 Python。我已将 requirements.txtsetup.py 添加到我的项目中。但是,我仍然对这两个文件的用途感到困惑。我读到 setup.py 是为可再分发的东西设计的,而 requirements.txt 是为不可再分发的东西设计的。但我不确定这是否准确。

这两个文件的真正用途是什么?

requirements.txt:

这有助于您设置开发环境。

pip这样的程序可以用来一次性安装文件中列出的所有包。之后你就可以开始开发你的 python 脚本了。如果您计划让其他人参与开发或使用虚拟环境,这将特别有用。 这是你如何使用它:

pip install -r requirements.txt

pip自己可以轻松制作:

pip freeze > requirements.txt

pip 自动尝试仅添加默认情况下未安装的包,因此生成的文件非常小。


setup.py:

这有助于您创建可以重新分发的程序包。

setup.py 脚本用于在最终用户的系统上安装您的包,而不是像 pip install -r requirements.txt 那样准备开发环境。有关 setup.py 的更多详细信息,请参阅 this answer


你的项目的依赖项在两个文件中都列出了。

简短的回答是 requirements.txt 仅用于列出包要求。 setup.py 另一方面更像是一个安装脚本。如果您不打算安装 python 代码,通常您只需要 requirements.txt

文件 setup.py 除了包依赖性之外,还描述了应该打包(或编译,在本机模块的情况下(即用 C 编写))的文件和模块集,和元数据添加到 python 软件包列表(例如软件包名称、软件包版本、软件包描述、作者……)。

因为两个文件都列出了依赖项,这可能会导致一些重复。阅读下文了解详情。

requirements.txt


此文件列出了 python 软件包要求。它是一个纯文本文件(可选地带有注释),其中列出了 python 项目的包 dependencies(每行一个)。它没有描述您的python包的安装方式。您通常会使用 pip install -r requirements.txt.

来使用需求文件

文本文件的文件名是任意的,但通常 requirements.txt 按照惯例。在浏览其他 python 包的源代码存储库时,您可能会偶然发现其他名称,例如 dev-dependencies.txtdependencies-dev.txt。这些与 dependencies.txt 的目的相同,但通常会列出特定包的开发人员感兴趣的其他依赖项,即在发布前测试源代码(例如 pytest、pylint 等)。软件包的用户通常不需要 运行 软件包的整套开发人员依赖项。

如果存在多个requirements-X.txt 变体,那么通常一个会列出 运行 时间依赖性,另一个 build-time 或测试依赖性。一些项目还级联它们的需求文件,即当一个需求文件包含另一个文件时 (example)。这样做可以减少重复。

setup.py


这是一个 python 脚本,它使用 setuptools 模块来定义 python 包(名称、包含的文件、包元数据和安装)。它会像 requirements.txt 一样列出包的 运行 时间依赖性。 Setuptools 是构建和安装 python 软件包的 de-facto 方式,但它有其缺点,随着时间的推移,它催生了新的 "meta-package managers" 开发,例如 pip。 setuptools 的示例缺点是它无法安装同一包的多个版本,并且缺少卸载命令。

当 python 用户执行 pip install ./pkgdir_my_module(或 pip install my-module)时,pip 将在给定目录(或模块)中 运行 setup.py。类似地,任何具有 setup.py 的模块都可以安装 pip,例如来自同一文件夹的 运行ning pip install .

我真的需要两者吗?


简短的回答是否定的,但两者兼有也很好。它们实现不同的目的,但它们都可用于列出您的依赖项。

您可以考虑一种技巧来避免重复 requirements.txtsetup.py 之间的依赖项列表。如果您已经为您的包编写了一个完全可用的 setup.py,并且您的依赖项主要是外部的,您可以考虑使用一个简单的 requirements.txt,仅包含以下内容:

 # requirements.txt
 #
 # installs dependencies from ./setup.py, and the package itself,
 # in editable mode
 -e .

 # (the -e above is optional). you could also just install the package
 # normally with just the line below (after uncommenting)
 # .

-e 是一个特殊的 pip install 选项,它以 editable 模式安装给定的包。当此文件上的 pip -r requirements.txt 为 运行 时,pip 将通过 ./setup.py 中的列表安装您的依赖项。 editable 选项将在您的安装目录中放置一个符号链接(而不是 egg 或存档副本)。它允许开发人员从存储库中就地编辑代码而无需重新安装。

当您的软件包存储库中同时包含这两个文件时,您还可以利用所谓的 "setuptools extras"。您可以在自定义类别下的 setup.py 中定义可选包,然后使用 pip:

从该类别中安装这些包
# setup.py
from setuptools import setup
setup(
   name="FOO"
   ...
   extras_require = {
       'dev': ['pylint'],
       'build': ['requests']
   }
   ...
)

然后,在需求文件中:

# install packages in the [build] category, from setup.py
# (path/to/mypkg is the directory where setup.py is)
-e path/to/mypkg[build]

这会将您的所有依赖项列表保留在 setup.py 中。

注意:您通常会从沙箱中执行 pip 和 setup.py,例如使用程序 virtualenv 创建的沙箱。这将避免在项目开发环境的上下文之外安装 python 包。

为了完整起见,这是我在 3 4 个不同角度看到的。

  1. 他们的设计目的不同

这是引用自official documentation(强调我的)的精确描述:

Whereas install_requires (in setup.py) defines the dependencies for a single project, Requirements Files are often used to define the requirements for a complete Python environment.

Whereas install_requires requirements are minimal, requirements files often contain an exhaustive listing of pinned versions for the purpose of achieving repeatable installations of a complete environment.

但它可能仍然不容易理解,所以在下一节中,将有 2 个实例来说明这两种方法应该如何不同地使用。

  1. 因此它们的实际用法(应该)不同
  • 如果您的项目 foo 将作为独立库发布(也就是说,其他人可能会这样做 import foo),那么您(和您的下游用户)会希望有一个灵活的依赖声明,这样你的库就不会(也不能)对你的依赖的确切版本应该是“挑剔的”。因此,通常,您的 setup.py 会包含这样的行:

         install_requires=[
             'A>=1,<2',
             'B>=2'
         ]
    
  • 如果您只是想以某种方式“记录”或“固定”您应用程序的确切当前环境bar,意思是,您或您的用户想要使用您的应用程序bar 按原样,即 运行 python bar.py,您可能想要冻结您的环境,以便它始终表现相同。在这种情况下,您的需求文件将如下所示:

         A==1.2.3
         B==2.3.4
         # It could even contain some dependencies NOT strickly required by your library
         pylint==3.4.5
    
  1. 现实中,我用哪一个?

    • 如果您开发的应用程序 bar 将被 python bar.py 使用,即使那是“只是为了好玩而写的脚本”,仍然建议您使用 requirements.txt 因为,谁知道呢,下周(恰好是圣诞节)你会收到一台新电脑作为礼物,所以你需要在那里重新设置你的确切环境。

    • 如果您正在开发将由 import foo 使用的库 foo,您必须准备一个 setup.py。时期。 但是您仍然可以选择同时提供一个requirements.txt,这样可以:

      (a) 要么采用 A==1.2.3 风格(如上文#2 所述);

      (b) 或者只包含一个神奇的单曲 .

         .
      

      后者本质上是使用常规的requirements.txt习惯来记录你的安装步骤是pip install .,意思是“按照setup.py的要求安装”而不重复。我个人认为最后一种方法有点模糊界限,增加了混乱,但它仍然是一种方便的方法,可以在 运行 在 CI 环境中明确选择依赖固定。这个技巧源自 Python 包装维护者 Donald 在 his blog post.

      中提到的方法
  2. 不同的下限。

    假设现有 engine 库具有以下历史记录:

    engine 1.1.0 Use steam
    ...
    engine 1.2.0 Internal combustion is invented
    engine 1.2.1 Fix engine leaking oil
    engine 1.2.2 Fix engine overheat
    engine 1.2.3 Fix occasional engine stalling
    
    engine 2.0.0 Introducing nuclear reactor
    

    您遵循以上 3 个标准并正确地决定您的新库 hybrid-engine 将使用 setup.py 声明其依赖项 engine>=1.2.0,<2,然后您的分离应用程序 reliable-car 将使用 requirements.txt 来声明其依赖项 engine>=1.2.3,<2(或者您可能只想固定 engine==1.2.3)。如您所见,您对它们的下限数字的选择仍然略有不同,它们都没有使用最新的 engine==2.0.0。这就是原因。

    • hybrid-engine 依赖于 engine>=1.2.0 因为,所需的 add_fuel() API 是在 engine 1.2.0 中首次引入的,并且该功能是hybrid-engine 的必要性,无论该版本中是否存在一些(小)错误并在后续版本 1.2.1、1.2.2 和 1.2.3 中得到修复。

    • reliable-car 取决于 engine>=1.2.3 因为这是迄今为止没有已知问题的最早版本。当然在更高版本中有新功能,即 engine 2.0.0 中引入的“核反应堆”,但它们不一定适合项目 reliable-car。 (您的另一个新项目 time-machine 可能会使用 engine>=2.0.0,但这是另一个主题。)

TL;DR

  • requirements.txt 列出具体依赖关系
  • setup.py 列出抽象依赖项

在 Python 中关于依赖管理的一个常见误解是您是否需要使用 requirements.txt setup.py 文件才能按顺序使用处理依赖关系。

您可能必须同时使用两者以确保在您的Python项目中适当地处理依赖关系。

requirements.txt 文件应该列出 具体的依赖项 。换句话说,它应该列出固定的依赖项(使用 == 说明符)。然后将使用此文件来创建一个工作的虚拟环境,该环境将安装所有具有指定版本的依赖项。

另一方面,setup.py 文件应该列出 抽象依赖项 。这意味着它应该列出 运行 项目的最小依赖项。除了依赖管理之外,这个文件还服务于包分发(比如在 PyPI 上)。

如需更全面的阅读,您可以阅读 TDS 上的文章 requirements.txt vs setup.py in Python


现在从 PEP-517 和 PEP-518 开始,您可能必须使用 pyproject.toml 来指定您想要使用 setuptools 作为 build-tool 和一个额外的 setup.cfg 文件来指定详细信息。 有关更多详细信息,请阅读文章 setup.py vs setup.cfg in Python.