Requirements.txt vs setup.py

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

这两个文件究竟应该如何使用?

89811 次浏览

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的更多详细信息,请参阅这个答案


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

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

setup.py文件除了描述包的依赖关系外,还描述了应该打包的文件和模块的集合(在本机模块的情况下是编译的(即用C编写的)),以及要添加到python包列表中的元数据(例如包名、包版本、包描述、作者等等)。

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


该文件列出了python包的要求。它是一个纯文本文件(可选带有注释),列出了python项目的包依赖关系(每行一个)。它描述了python包的安装方式。你通常会使用pip install -r requirements.txt来使用需求文件。

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

如果存在多个__abc0变量,那么通常一个将列出运行时依赖项,而另一个将列出构建时或测试依赖项。有些项目还会级联它们的需求文件,即当一个需求文件包含另一个文件(例子)时。这样做可以减少重复。

setup . py


这是一个python脚本,使用setuptools模块定义一个python包(名称,包含的文件,包元数据和安装)。像requirements.txt一样,它也会列出包的运行时依赖项。Setuptools实际上是构建和安装python包的方法,但它也有缺点,随着时间的推移,这些缺点催生了新的“元包管理器”,比如pip。setuptools的缺点是它不能安装同一个包的多个版本,并且缺少卸载命令。

当python用户执行pip install ./pkgdir_my_module(或pip install my-module)时,pip将在给定目录(或模块)中运行setup.py。类似地,任何具有setup.py的模块都可以被__abc4安装,例如,通过在同一文件夹中运行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选项,它以可编辑的模式安装给定的包。当pip -r requirements.txt在这个文件上运行时,pip将通过./setup.py中的列表安装你的依赖项。可编辑选项将在安装目录中放置符号链接(而不是egg或存档副本)。它允许开发人员在存储库中编辑代码,而无需重新安装。

当你的包存储库中有这两个文件时,你还可以利用所谓的“setuptools额外功能”。你可以在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. 它们的设计目的不同

这是引用自官方文档的精确描述(强调我的):

尽管install_requires(在setup.py中)定义了依赖项对于单个项目,需求文件通常用于定义需求获取一个完整的Python环境

尽管install_requires需求很少,但需求文件通常包含固定版本的详尽列表,以实现完整环境的可重复安装。

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

  1. 因此,它们的实际用法是不同的
  • < p >如果您的项目foo是作为一个独立的库发布(意义,其他人可能会做import foo),你(和你的下游用户)想要有一个灵活的声明依赖性,因此,图书馆不会(也不能)是“picky"你的依赖项应该是什么版本。所以,通常,你的setup.py会包含这样的行:

         install_requires=[
    'A>=1,<2',
    'B>=2'
    ]
    
  • 如果你只是想以某种方式“文档”;或“;pin"你的应用程序的确切当前环境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. 在现实中,我用哪个?

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

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

      (a)要么是A==1.2.3风格(如上面的#2所解释);

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

         .
      

      后者本质上是使用传统的requirements.txt习惯来记录你的安装步骤是pip install .,这意味着“根据setup.py安装需求”。而没有复制。就我个人而言,我认为最后一种方法有点模糊界限,增加了混乱,但它仍然是在CI环境中运行时显式选择不使用依赖项固定的方便方法。这个技巧来源于Python打包维护者Donald在他的博客文章中提到的一种方法。

  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;

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

关于Python中的依赖项管理,一个常见的误解是是否需要使用requirements.txt setup.py文件来处理依赖项。

你可能不得不同时使用,以确保在你的Python项目中适当地处理依赖项。

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

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

更全面的阅读,你可以阅读文章requirements.txt与Python中的setup.py在TDS。


现在继续前进,从PEP-517和PEP-518开始,你可能必须使用pyproject.toml来指定你想要使用setuptools作为构建工具,并使用额外的setup.cfg文件来指定细节。 有关更多详细信息,您可以阅读文章Python中的setup.py vs setup.cfg.