requirements.txt与setup.py


110

我开始使用Python。我已经添加了requirements.txtsetup.py我的项目。但是,我仍然对两个文件的目的感到困惑。我读过,它setup.py是为可再发行的事物而requirements.txt设计的,并且是为不可再发行的事物而设计的。但是我不确定这是正确的。

如何真正使用这两个文件?


1
您是否使用准确的标题搜索过网络?这篇文章(我搜索时的第一)是我读过的最好的文章。
克里斯(Chris)2007年

2
这篇文章可能有用:caremad.io/posts/2013/07/setup-vs-requirement(对不起,太懒了,无法将要素提取为正确的答案)。另一件事是,某些工具(例如测试)可能会对一个或另一个工具有偏见-但是,如果您刚开始使用Python,则不要让它困扰您。
drdaeman

Answers:


82

requirements.txt

这可以帮助您设置开发环境。诸如此类的程序pip可用于一次安装文件中列出的所有软件包。之后,您可以开始开发python脚本。如果您计划让其他人参与开发或使用虚拟环境,则特别有用。这是您的用法:

pip install -r requirements.txt

setup.py

这使您可以创建可以重新分发的软件包。该脚本旨在将软件包安装在最终用户的系统上,而不是像在准备开发环境那样pip install -r requirements.txt。有关setup.py的更多详细信息,请参见此答案

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


2
在哪种情况下,我只有其中之一?我都在哪?
Martin Thoma

26
嗯...您只是在本地计算机上编写了有趣的脚本:都不是。脚本是在多台机器/ vitualenvs上开发的,但未重新分发:requirements.txt。脚本仅在您的计算机上开发,但应重新分发:setup.py。脚本将在多种环境中重新分发和开发:两者。
AndreasT

您可以将此添加到答案中吗?
马丁·托马

58

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

该文件setup.py除了描述软件包的依赖关系之外,还描述了应打包(或编译,对于本机模块(即,用C编写)的文件和模块)和添加到python软件包列表中的元数据(例如,程序包名称,程序包版本,程序包描述,作者等)。

因为两个文件都列出了依赖性,所以这可能会导致一些重复。请阅读下面的详细信息。

requirements.txt


该文件列出了python软件包的要求。这是一个纯文本文件(可选带注释),列出了python项目的程序包依赖项(每行一个)。它没有描述python软件包的安装方式。通常,您将使用消耗需求文件pip install -r requirements.txt

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

如果requirements-X.txt存在多个变体,则通常一个将列出运行时依赖性,而另一个将列出运行时依赖性或测试依赖性。一些项目还会层叠其需求文件,即一个需求文件包含另一个文件时(例如)。这样做可以减少重复。

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可以pip安装任何具有的模块,例如,pip install .从同一文件夹运行。

我真的需要两者吗?


简短的答案是没有,但同时拥有它们是很好的。它们实现了不同的目的,但是都可以用来列出您的依赖项。

您可能需要考虑一种技巧,以避免在requirements.txt和之间复制依赖项列表setup.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 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,例如使用program创建的virtualenv。这样可以避免在项目开发环境的上下文之外安装python软件包。


7
你也可以有刚刚.W / O -erequirements.txt。此方法仅将所有需求委托给setup.py您,而无需强迫任何人进入可编辑模式。用户仍然可以这样做pip install -e .
stason

1
“ -e”有趣的把戏。在requirements.txt中,但是这是否违反了requirements.txt的目的,即确切的系统规格?在这种情况下为什么还要一个?
Ben Ogorek

您可以在setup.py中拥有确切的系统要求。有“。” Requirements.txt中的内容使其使用当前文件夹中的setup.py。使用-e .还会使用setup.py查找依赖项,但是将当前文件夹(使用符号链接)链接到pip安装文件夹中,而不是复制副本- -e通常仅在开发软件包时使用。使用-e,对python软件包文件(* .py)的更改将在您的pip环境中立即生效,而无需在每次更改后强制重新安装软件包。
init_js

@init_js是相对于需求文件或CWD的“当前文件夹”,从中调用pip?即如果您愿意,cd foo && pip install -r ./bar/requirements.txt它将在foo/bar或中搜索setup.py foo。如果是后者,有没有办法实现前者?
Dan M.

pip -r REQ不在乎REQ所在的目录。即使您愿意,也可以从fifo馈送它pip install -r <(echo "mylib1"; echo "mylib2";)<(CMD)bash命令替换在哪里,不是stdin重定向。
init_js

12

为了完整起见,以下是我从3 4个不同角度看待它的方法。

  1. 他们的设计目的是不同的

这是官方文档(重点是我的)中引用的精确描述:

尽管install_requires(在setup.py中)定义了单个项目的依赖关系,但“需求文件”通常用于定义完整Python环境的需求。

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

但是它可能仍然不容易理解,因此在下一节中,将提供2个事实示例,以不同的方式演示应如何使用这两种方法。

  1. 因此,它们的实际用法(应该是)不同

    • 如果您的项目foo将作为独立的库发布(意味着其他人可能会这样做import foo),那么您(和下游用户)将希望有一个灵活的依赖声明,以使您的库不会(而且一定不能) )对您的依赖项的确切版本“保持警惕”。因此,通常,您的setup.py将包含以下行:

      install_requires=[
          'A>=1,<2',
          'B>=2'
      ]
    • 如果您只是想以某种方式为您的应用程序“记录”或“固定”您的EXACT当前环境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
  2. 实际上,我该使用哪一个?

    • 如果您正在开发bar将由所使用的应用程序python bar.py,即使该应用程序只是“有趣的脚本”,仍然建议您使用requirements.txt,因为谁知道下周(恰好是圣诞节)您会收到新计算机作为礼物,因此您需要在此重新设置您的确切环境。

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

      (a)采用任何一种A==1.2.3风格(如上文第2条所述);

      (b)或只包含一个魔法单曲 .

      .

      大致等于“基于setup.py安装要求”,而无需重复。我个人认为这是最后一种方法,模糊了界限,增加了混乱,并没有真正增加价值,但这仍然是Python包装维护商Donald在他的博客文章中提到的一种方法。

  3. 下限不同。

    即使遵循了以上3条条件,并正确地决定了您的库hybrid-engine仍将使用a setup.py声明其依赖关系engine>=1.2.0,而示例应用程序reliable-car仍将使用requirements.txt其声明其依赖关系engine>=1.2.3,即使最新版本的engine1.4.0也是如此。如您所见,您对它们的下限数字的选择仍然略有不同。这就是为什么。

    • hybrid-engineengine>=1.2.0假设是因为,假设地,首先引入了所需的“内部燃烧”功能engine 1.2.0,而该功能是必不可少的hybrid-engine,而不管该版本内部是否存在某些(较小的)错误,并已在后续版本1.2.1中进行了修复。 ,1.2.2和1.2.3。

    • reliable-car取决于,engine>=1.2.3因为到目前为止,这是没有已知问题的最早版本。当然,以后的版本中会有新功能,例如引入了“电动机”和引入了engine 1.3.0“核反应堆” engine 1.4.0,但是对于项目而言,它们并不是必需的reliable-car


“您的库不会(也不应该)对您的依赖项的确切版本“挑剔”。” 您能详细说明一下吗?我猜您的代码通常仅使用特定版本的依赖项进行测试,这种方法可能有点危险。我假设一个库应该使用一系列版本,因为您不想安装太多版本的依赖项?节省磁盘空间?
Taro Kiritani

@TaroKiritani实际上,我并排列出了2种不同的方案,即库案例和应用案例。也许您以前没有在图书馆工作过?作为一个库,它有望被下游软件包所使用。因此,如果您挑剔要固定自己的依赖关系A==1.2.3,然后如果库的下游程序包恰好依赖于A==1.2.4,那么现在将无法同时满足这两个要求。最小化此冲突的解决方案是您的库定义一个已知的范围。假设semver.org之后已经有许多上游库,A>=1,<2那么它将起作用。
RayLuo

我没有意识到只能在一个环境中安装一个版本的软件包。stackoverflow.com/a/6572017/5686692感谢您的澄清。
Taro Kiritani

1
@TaroKiritani,是啊,否则怎么会你的应用程序知道哪个版本fooimport foo给你?在您提供的链接中那些令人讨厌的答案,可以很好地说明为什么软件包维护者“不应该也不能挑剔”。:-)我现在可以请你投票吗?
RayLuo

1
我也可以评论这个新想法,但是随后这个评论部分已经不合时宜,新手很难遵循。我建议您提出一个新问题:“我们应该使用毒物之类的东西来保证我的图书馆能够处理各种依赖关系
吗?
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.