requirements.txt 与 setup.py

Posted

技术标签:

【中文标题】requirements.txt 与 setup.py【英文标题】:requirements.txt vs setup.py 【发布时间】:2017-09-25 07:30:45 【问题描述】:

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

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

【问题讨论】:

您是否使用确切的标题搜索过网络? This article(我搜索时的第一次点击)是我读过的最好的主题。 这篇文章可能有用:caremad.io/posts/2013/07/setup-vs-requirement(抱歉,懒得将要点提取成正确的答案)。另一件事是,某些工具(例如测试)可能对其中一个或另一个有偏见 - 但如果您刚刚开始使用 Python,请不要让它打扰您。 如果我 conda/pip 安装每个包,与仅使用 .sh 脚本相比,这有什么优势(setup.pyrequirements.txt)? 【参考方案1】:

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。


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

【讨论】:

在哪些情况下我只有其中一个?我会在哪一个? Erm...您只是在本地计算机上编写有趣的脚本:两者都不是。脚本在多台机器/vitualenvs 上开发,但未重新分发:requirements.txt。脚本仅在您的机器上开发,但应重新分发:setup.py。脚本将在多种环境中重新分发和开发:两者。 你能把这个添加到答案中吗? 如果没有requirements.txt,你真的会拥有setup.py吗?求一个完全不懂这些东西的朋友。 在开发者端,为什么不使用 pip install -e 。匹配依赖项?【参考方案2】:

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

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

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

requirements.txt


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

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

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

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.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 中的列表安装你的依赖项。可编辑选项将在您的安装目录中放置一个符号链接(而不是鸡蛋或存档副本)。它允许开发人员从存储库中编辑代码,而无需重新安装。

当您的软件包存储库中同时拥有这两个文件时,您还可以利用所谓的“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 包。

【讨论】:

您也可以在requirements.txt 中只包含. w/o -e。此方法只是将所有要求委托给setup.py,您无需强制任何人进入可编辑模式。如果愿意,用户仍然可以使用pip install -e . 使用“-e”的有趣技巧。在 requirements.txt 中,但这不会破坏 requirements.txt 作为确切系统规范的目的吗?在那种情况下为什么还要有一个? 您可以在 setup.py 中找到确切的系统要求。有“。”在 requirements.txt 中使它使用当前文件夹中的 setup.py。使用 -e . 也使用 setup.py 来查找依赖项,但在 pip 安装文件夹中链接当前文件夹(就地,带有符号链接),而不是复制 - 通常只有在你使用 -e '正在开发包。使用-e,对 python 包文件 (*.py) 的更改将在您的 pip 环境中立即生效,而不必在每次更改后强制重新安装包。 @init_js 是相对于调用 pip 的需求文件或 CWD 的“当前文件夹”? IE。如果您执行cd foo && pip install -r ./bar/requirements.txt,它会在foo/barfoo 中搜索setup.py 吗?如果是后者,有没有办法实现前者? @BenOgorek 库只需要 setup.py,不需要 requirements.txt。您不想在人为约束的开发环境中编写库(具有来自 requirements.txt 的更严格的版本约束),因为您的用户不会有如此整洁的情况。他们只能通过 setup.py 的镜头体验生活。但是一些 CI 系统需要一个 requirements.txt 文件。在这种情况下,点技巧会有所帮助。【参考方案3】:

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

    它们的设计目的不同

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

而 install_requires(在 setup.py 中)定义了单个项目的依赖项,而需求文件通常用于定义完整的 Python 环境的要求。。 p>

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

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

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

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

     install_requires=[
         'A>=1,<2',
         'B>=2'
     ]

如果您只是想以某种方式“记录”或“固定”您的应用程序bar 的确切当前环境,这意味着您或您的用户希望按原样使用您的应用程序bar,即运行@987654329 @,您可能想冻结您的环境,使其始终表现相同。在这种情况下,您的需求文件将如下所示:

     A==1.2.3
     B==2.3.4
     # It could even contain some dependencies NOT strickly required by your library
     pylint==3.4.5

    实际上,我该使用哪一个?

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

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

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

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

       .
    

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

    不同的下界。

    假设存在具有此历史记录的现有 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&gt;=1.2.0,&lt;2,然后您的分离应用程序 reliable-car 将使用 requirements.txt 声明其依赖关系engine&gt;=1.2.3,&lt;2(或者您可能只想固定 engine==1.2.3)。如您所见,您选择的下限数字仍然略有不同,并且它们都没有使用最新的engine==2.0.0。这就是原因。

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

    reliable-car 依赖于engine&gt;=1.2.3,因为这是迄今为止没有已知问题的最早版本。当然,在以后的版本中会有新的功能,例如engine 2.0.0 中引入的“核反应堆”,但它们不一定适合reliable-car 项目。 (您的另一个新项目time-machine 可能会使用engine&gt;=2.0.0,但这是一个不同的主题。)

【讨论】:

"你的库不会(也不能)对你的依赖的确切版本应该是什么“挑剔”。您能否详细说明这一点?我猜您的代码通常只使用特定版本的依赖项进行测试,这种方法可能有点危险。我假设一个库应该适用于一系列版本,因为您不想安装太多版本的依赖项?节省磁盘空间? @TaroKiritani,是的,否则你的应用程序怎么知道import foo 给你的是哪个版本的foo? that link you provided 中的那些 hacky 接受的答案是一个完美的例子,说明为什么包维护者“不应该也不应该挑剔”。 :-) 现在我可以投票了吗? 我也可以对这个新想法发表评论,但是这个 cmets 部分已经跑题了,新手很难跟上。我建议你问一个新问题“我们是否应该使用 tox 或其他东西来保证我的库适用于各种依赖项组合”,然后人们可以加入。 @CharlieParker, “如果你正在开发一个库 foo 将被 import foo 使用,你必须准备一个setup.py。句号。”因为您的下游库或应用程序将无法触发您的 .sh 脚本。但是,根据您的问题,我猜您不是图书馆开发人员。然后,是的,您可以随心所欲,只需为最终用户记录安装步骤即可。不过,我仍然会争辩说,每行一个依赖项 requirements.txt.sh 更通用和 Pythonic。如果您的最终用户在无法运行 .sh 的 Windows 上怎么办? 非常有用的答案 - 并链接到 Donald Stufft 博客文章。为我解决了一个问题,谢谢。我确实认为您低估了库开发人员(我)在 requirements.txt 中使用魔法点技巧的价值。作为库开发人员,我不需要/想要 requirements.txt,因为我的用户只会体验 setup.py。但是,一些 CI 系统需要它。因此,无需复制依赖项列表,魔术点将使我的生活更简单。

以上是关于requirements.txt 与 setup.py的主要内容,如果未能解决你的问题,请参考以下文章

python 项目自动生成 requirements.txt 文件

将需求从 requirements.txt 添加到 conda meta.yaml

requirements的生成与用法

原 requirements.txt 介绍 & 快捷生成

如何自动生成和安装requirements.txt依赖

转载如何自动生成和安装requirements.txt依赖