Pipenv: 如何保持 setup.py install_requires 和 Pipfile 同步

创建于 2018-01-03  ·  49评论  ·  资料来源: pypa/pipenv

我正在使用 pipenv 开发 Python 包,并且面临保持setup(install_requires=...)与我的 Pipfile 的运行时依赖项同步的挑战。 有推荐的方法吗?

[回答 2019-08-23]最佳实践如下所述:

对于在安装程序中部署或分发的应用程序,我只使用 Pipfile。

对于使用 setup.py 作为包分发的应用程序,我将所有依赖项放在 install_requires 中。

然后我通过运行pipenv install '-e .'使我的 Pipfile 依赖于 setup.py 。

最有用的评论

对于在安装程序中部署或分发的应用程序,我只使用 Pipfile。

对于使用 setup.py 作为包分发的应用程序,我将所有依赖项放在 install_requires 中。

然后我通过运行pipenv install '-e .'使我的 Pipfile 依赖于 setup.py。

[2019-08-23 更新] 现在我将开发包保存在 Pipfile 中,只有运行时依赖项才能存在于 setup.py 中。

所有49条评论

pipenv 是否有可以使用的 python API? 我在处理项目时手动更新列表,但以下内容可能很好:

from setuptools import setup
from pipenv import find_install_requires

setup(
    # ...
    install_requires=find_install_requires()
    # ...
)

该函数只需要返回 pipfiles [packages] 部分中的键列表。 我想你已经可以使用辅助函数来实现这个功能,但是如果它是 pipenv 的一部分会很好,所以我们不必都实现它。

Pipfile ,支持 Pipenv 的 Pipfile 解析的实现,可以帮助解决这个问题:

import pipfile
pf = pipfile.load('LOCATION_OF_PIPFILE')
print(pf.data['default'])

但我不推荐这个,或者依赖于 setup.py 中的 Pipenv。 导入pipenv (或pipfile )意味着用户需要在尝试安装软件包之前实际安装它,而像 Pipenv 这样的工具试图在不安装的情况下窥视它( setup.py egg_info )赢了不行。 setup.py 应该只依赖于 Setuptools。

一个中间的解决方案是编写一个类似于bumpversion的工具,它可以自动同步基于 Pipfile 的文本文件。 将此文件与您的包一起分发,并在 setup.py 中读取它。 然后使用 CI 或提交挂钩来确保文件始终同步。

是的,好点忽略我。
也许“pipenv install”可以进行同步?

2018 年 1 月 8 日星期一下午 5:04,Tzu-ping Chung [email protected]
写道:

Pipfile https://github.com/pypa/pipfile ,实现支持
解析,可以帮助解决这个问题:

导入点文件
pf = pipfile.load('LOCATION_OF_PIPFILE')
打印(pf.data ['默认'])

但我不推荐这个,或者依赖于 setup.py 中的 Pipenv。
导入 pipenv(或 pipfile)意味着用户需要实际安装
在尝试安装你的包之前,以及像 Pipenv 这样的工具试图
在不安装 (setup.py egg_info) 的情况下查看它是行不通的。 这
setup.py 应该只依赖于 Setuptools。

一个中间的解决方案是编写一个类似于bumpversion的工具
https://github.com/peritus/bumpversion自动同步文本
基于 Pipfile 的文件。 将此文件与您的包一起分发,并阅读它
在 setup.py 中。 然后使用 CI 或提交挂钩来确保文件始终
同步中。


您收到此消息是因为您发表了评论。
直接回复此邮件,在 GitHub 上查看
https://github.com/pypa/pipenv/issues/1263#issuecomment-355889369或静音
线程
https://github.com/notifications/unsubscribe-auth/ALMBlmmV52kdIL9D4zlMJoQh2JpaGdDbks5tIa_jgaJpZM4RRu3v
.

@uranusjr只是在这里测试我的假设,但是是否可以将 pipenv 添加到 setup.py 的 setup_requires,并将 pipenv 导入延迟到 setuptools 命令? 或者这被认为是不好的做法?

@Korijn本身可能不是,但鉴于当前的最佳实践是为每个 Python 项目使用单独的 virtualenv,这将需要用户为每个项目安装 Pipenv 的副本,这不是很直观。 Pipenv 应该只安装一次(通常是全局的),并且在项目的 virtualenv 之外使用来管理它,而不是在项目的 virtualenv 内部。

那么导致该问题关闭的解决方案是什么? 有没有办法同时跟踪Pipfilesetup.py中的依赖关系? 是否有绕过该问题的最佳实践?

对于在安装程序中部署或分发的应用程序,我只使用 Pipfile。

对于使用 setup.py 作为包分发的应用程序,我将所有依赖项放在 install_requires 中。

然后我通过运行pipenv install '-e .'使我的 Pipfile 依赖于 setup.py。

[2019-08-23 更新] 现在我将开发包保存在 Pipfile 中,只有运行时依赖项才能存在于 setup.py 中。

我认为@Korijn 的方法是这里的最佳实践。 Pipfile(和 requirements.txt)用于应用程序; setup.py 用于包。 它们服务于不同的目的。 如果您需要同步它们,那么您做错了(IMO)。

@uranusjr不是根据文档。

Pipenv 是一个工具,旨在将所有打包世界中最好的(打包程序、作曲家、npm、货物、纱线等)带到 Python 世界。 在我们的世界中,Windows 是一等公民。

它会自动为您的项目创建和管理 virtualenv,并在您安装/卸载包时从您的 Pipfile 中添加/删除包。 它还会生成非常重要的 Pipfile.lock,用于生成确定性构建。

也许我只是不明白。 请您详细说明您的陈述好吗?

我理解它的方式是pipenv是一个完整的依赖管理系统,类似于 PHP 的作曲家,但我开始意识到它不是。 尤其是pipenv不会安装具有 Pipfile 和 Pipfile.lock 但在 setup.py 中没有 install_requirements 的依赖项的依赖项。

@vascowhite你问的问题不是关于pipenv,而是关于python打包工具之间的基本分离。 在 python 工作流程中, setup.py文件用于声明可分发包的安装依赖项。 所以,如果我有一个像requests这样的包,并且它取决于安装cffi的人,我会在我的setup.py中声明,这样当人们运行pip install requests如有必要,它也会执行该安装。

Pipfile 和需求文件一样,并不意味着递归遍历。 相反,有一个 pipfile 来管理您可能正在开发的项目的所有依赖项。 重点是旧的工作流程生成了一个固定需求的扁平化列表,而 Pipfiles 包含顶级需求,并且在可能的情况下更喜欢未固定的需求。 当您安装一个包时,它的setup.py的要求递归地解析为也符合您其他要求的最佳匹配。

所以如果你想知道为什么 Pipfiles 没有被递归解析,那是因为这不是它们在 python 中的使用方式。 运行pipenv install最终需要一个可由setuptools安装的目标,这意味着它将在其安装文件中定义其安装要求。

@techalchemy在你出现之前,我已经完成了类似的回复😂(删除所有内容)

我还想指出,您所问的@vascowhite实际上并不奇怪。 由于 Pipfile 和锁定文件都可用,因此可以协调两个不同的工作流程。 理想情况下,Pipfile 替换 setup.py 的install_requires ,并用于指定虚拟依赖项,而 lock 文件用于生成基于它的具体依赖项集,替换 requirements.txt。

然而,Python 的打包系统目前远非理想,在这之前需要进行大量清理工作。 哎呀,Pipenv现在已经很难处理依赖关系了(ps不是任何人的错),除非像这样使用最简单的项目,否则它可能几乎无法工作。

希望并没有失去(至少不是我的)。 围绕这个问题已经提出并实施了很多 PEP,我觉得一切都在正确的轨道上,setup.py 和 requirements.txt 都朝着严格的声明性格式发展。 在如此庞大的生态系统中,事情需要缓慢发展(或参见 Python 3.0),但确实在发展。

@techalchemy @uranusjr
谢谢你们两位的明确回答,他们确实理顺了我的一些想法。 在我看来,文档已经说明了 Pipenv 能够做什么,这部分是我困惑的原因。 不过,我的大部分困惑都归咎于我:)

来自 PHP 的我一直被 Python 中的打包弄糊涂,相比之下,Composer 是轻而易举的事。 我确实发现 python 更容易开发并且喜欢使用它。 让我们希望事情有所改善,我相信他们会付出像你们和肯尼斯·赖茨这样的人的努力。

如果你坚持我上面提到的建议,你可以完美地协调 setup.py 和 pipenv。 没必要大惊小怪。 :)

看来不是我一个人糊涂了#1398

虽然比我做得更好:)

来这里获取有关使用pipenvsetup.py的信息; 在讨论中添加我的 0.2 美分。

我有一个 python 包,它的setup.py看起来像:

 setup(                                                                                                     
    name='my-pkg-name',                                                                             
    packages=find_packages(),                                                                              
    install_requires=[...],
    extras_require={
        'develop': ['click']
    },
    entry_points={
        'console_scripts': [
            'my-pkg-name-cmdline = my-pkg-name.cli:tool'
        ]
    }

如您所见,我在脚本入口点中使用click来执行构建和部署等任务。

当我运行$ my-pkg-name-cmdline build时,我没有找到click安装,因为pipenv install --dev在 pipenv virtualenv 中安装包。 我需要摆弄pipenv shell/exit以使其正常工作。 看起来这方面仍有一些粗糙的边缘。

因此 +1 表示不使用pipenv包。

我认为你应该在这种情况下打电话给pipenv run my-pkg-name-cmdline build

@Korijn我仍然不确定正确的工作流程(仍在尝试使用 pipenv)。

到目前为止,似乎对我有用的工作流程是:

(starting from scratch)
1- pipenv --three
2- pipenv install [--dev]
3- pipenv install -e . (install application locally)
4- pipenv shell (to enter the virtualenv)

现在我可以从命令行运行我的包构建click脚本。

如果我在本地安装应用程序(步骤 3)之前进入 virtualenv(步骤 4),它不起作用。

也许我只需要重新连接我的大脑,记住应该在pipenv shell之前安装软件包(而使用virtualenv需要您安装激活了 virtualenv 的软件包)。

@apiraino我认为您在这里没有得到正确的结果。 如果你想在你的包中使用(导入)click,你应该把它放在install_requires中,这样安装你的包的人(包括你自己)也可以安装 click。 将它放入extras_require['dev']意味着它是一个可选依赖项,即您的包可以在没有它的情况下工作,但是安装这些附加功能可以提供某些额外的功能。

这个讨论真的和 Pipenv 没有任何关系了。 我建议你把这个问题带到更合适的论坛,比如 StackOverflow 或 Python 相关的邮件列表。

@Korijn pipenv install '-e .'产生Pipfile不反映 setup.py 中 install_requires 下列出的模块

pipenv 9.0.3 仍然是这种情况。

如何从我的setup.py的 install_requires 生成Pipfile

不要使用引号

我停止使用引号。 但是,我没有创建一个包含来自install_requires Pipfile的部门的setup.py

@benjaminweb 今天我对同样的事情感到困惑。 但是,我开始认为当前的行为可能会正确。

@techalchemy上面提到过

Pipfiles 包含顶级要求,并在可能的情况下首选未固定。 当您安装一个包时,它的 setup.py 中的要求会递归地解析为也符合您的其他要求的最佳匹配。

如果您使用https://github.com/pypa/pipenv/issues/1263#issuecomment -362600555 中提到的工作流程,当您在没有现有 Pipfile 的项目上运行pipenv install '-e .'时,pipenv 会生成一个新的 Pipfile下列:

[packages]

"e1839a8" = {path = ".", editable = true}

在这种情况下,您明确要求安装到 virtualenv 中的唯一包是包本身(即“.”),因此只有“.”才有意义。 被添加到 Pipfile 中的 ( [packages] ) 中。 同样,如果您pipenv install requests ,则请求的 setup.py 中的install_requires依赖项也不会添加到项目的 Pipfile 中。

但是,当包安装步骤接下来发生时, install_requires依赖项作为package 依赖项解析的一部分安装。

请注意,与 Pipfile 不同的是,Pipfile.lock 记录了整个 virtualenv 的所有确切依赖项,其中必须包括锁定到特定版本的install_requires依赖项。 如果您查看生成的 Pipfile.lock,您将看到列出的install_requires依赖项。

我可能完全误解了这是如何工作的。 也许@techalchemy@uranusjr可以确认这是否是正确的思考方式?

你的思路和我的一致。 我还要提到,通过最近的 Setuptools 改进和 Flit 等工具,您仍然可以以漂亮的 TOML 形式指定包的依赖项(而不是 setup.py 中的要求字符串,这当然不是很漂亮)。 您只需在 pyproject.toml 而不是 Pipfile 中指定它们。

@uranusjr听起来您的意思是,如果项目依赖项尚未被 Setuptools 或 Flit 等打包工具捕获(通过 setup.py 或 pyproject.toml),则 Pipfile 只需要显式列出项目依赖项

例如,如果 setup.py 看起来像:

install_requires=['A'],
extras_require={
    'dev': ['B'],
},

那么 Pipfile 只需要以下内容:

[[source]]

url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"


[packages]

"e1839a8" = {path = ".", editable = true}


[dev-packages]

"e1839a8" = {path = ".", extras = ["dev"], editable = true}

运行pipenv install将为生产安装依赖项 A,而pipenv install --dev将为开发安装依赖项 A 和 B。

如果有人已经在使用 Setuptools 或 Flit,是否有任何理由将依赖项添加到[packages][dev-packages]下的 Pipfile 中? 以Requests为例,我不清楚为什么开发依赖项在[dev-packages]下的 Pipfile 中明确列出,但是install_requirestest_requirements依赖项都在 setup 中捕获.py。

似乎您需要显式向 Pipfile 添加依赖项的唯一原因是您使用 Setuptools 或 Flit。 这个对吗? 这不是真的有什么理由吗?

我认为这只是个人喜好。 在extras_require[dev]中列出开发依赖项只是一种约定; dev-packages OTHO 是一个定义明确的密钥。 extras_require[dev]还可以让任何用户使用pip install package[dev] ,这可能是维护者不喜欢的。 我可以理解人们更喜欢其中之一。

至于packages ,不,真的没有比install_requires IMO 更有意义的场景了。 我相信人们会想出创造性的用法。

为什么这个问题被关闭了?

@JulienPalard由于以下几个原因,它已关闭:

  1. Pipfiles 和setup.py文件并不是真的要保持同步,就其本身而言,我认为已经链接了很多文章来讨论细节,但是 tl;dr Pipfile大致相当于顶级requirements.txt
  2. 如果您希望您的项目(比如说它是当前目录)将其setup.py解析到托管的 virtualenv 中,则工作流程将只是pipenv install -e . - 这会将单个条目放入您的Pipfile (顶级根)和已解析的依赖关系图到您的Pipfile.lock中,包括已解析的install_requires 。 如果由于install_requires更改而想用最新的内容更新 virtualenv,则必须运行pipenv update ,这与最新版本中的pipenv lock && pipenv sync相同。

希望这有帮助!

实际上它们比Pipfile更类似于requirements.txtrequirements.txt以扁平方式指定所有包,而Pipfile & setup.py只需要入门级依赖项。 Pipfile.lock & requirements.txt包含类似的信息。

我创建了一个 POC 同步脚本,它可以进一步实现,但目前涵盖了我们的用例:
https://gist.github.com/iddan/f190c3c7d54f4fc4655da95fb185e641

@iddan这基本上就是我一直在说的,这些东西中的每一个都代表您的依赖项的顶级列表,但是一个用于_安装应用程序_( setup.py ),另一个用于_开发它_( Pipfile )。

在使用setup.py的前一种情况下,您可以使用与使用 $# requirements.txt 3$#$ 相同的选项来声明开放式或完全固定的依赖项,但大多数人在这里使用开放式依赖固定。 在Pipfile中,您也可以指定严格或松散的引脚,但同样鼓励将其保留为依赖关系图的 _unflattened_ 列表。 再次强调,这两个东西在requirements.txt文件中也是完全有效的,这就是为什么我不断强调应用程序和库之间的职责分离的原因。 你会在每次演讲、每个教程以及 PyPA 发布的所有消息中听到这一点的强调。

Pipfile.lockrequirements.txt不是一回事。 您可以从Pipfile.lock生成有效的requirements.txt $ ,但如果不使用中介Pipfile ,则不能直接从 requirementsfile 生成锁定文件。 这是因为Pipfile.lock代表一个传递闭包,并且总是需要一个中介Pipfile来执行依赖解析。

至于最初的问题,没有理由让setup.py文件与Pipfile $ 文件保持同步,而不是简单地将有问题的目录包含为可编辑路径。 当您运行pipenv lock时,将自动解析setup.py文件中的依赖项。 我不确定您的具体用例@iddan ,或者为什么您需要一个特殊的解析器来将内容写回设置文件,但我怀疑您可能想阅读Donald Stufft 关于 setup.py 与要求的文章。 txt或参考我们的一位维护人员关于区别以及它如何特别适用于 Pipenv 的评论。

我的用例是,在 K Health,我们有一个包含内部包的存储库,这些包是独立服务,也可以作为包使用。 所以我们想在包消费者和服务的开发/部署配置之间共享我们的顶级依赖关系。 由于我们使用 Pipenv 来管理我们的依赖关系,因此将Pipfile的输出作为setup.py会很好

听起来像是 #2148 的变体(将 requirements.txt 替换为 Pipfile)。

但这是关于setup.py

这。 问题。 应该。 不是。 是。 关闭。

此问题已关闭,因为

  1. Pipenv不是一个打包工具
  2. 已决定不在 Pipenv 中包含打包功能。
  3. 已经阐明了如何在包的开发过程中使用 Pipenv 来管理开发依赖项。
  4. Pipfile 是一个开放标准。 基于 Pipfile 格式构建打包工具很容易。 Pipenv 决定不这样做的事实并不意味着排除自己这样做。

如果你真的这么在乎,请自己制作一个工具。 由于对这个问题有如此多的期待,我相信如果您在此处发布您的解决方案,将不难获得关注。 并且有了牵引力,PyPA 可以像 Pipenv 一样在打包指南中推荐它。 但首先您需要实际构建该工具。

也请学会尊重地对待项目维护者。 请参阅行为准则以供参考。 我们很高兴进行富有成效的讨论,但我们是志愿者,我们在这里不是为了简单地服从个人意愿。 如果您无法做到这一点,请不要打扰发布。

我建议锁定此问题以防止进一步讨论。 我认为这一点已经说得很清楚了。

做一件事,把它做好!

大家好,

@Korijn我阅读了您解释如何使用 extra_requires 将 setup.py 与 Pipfile 同步的部分。

我试图这样做,并注意到 Pipfile.lock 在 dev 部分中没有 extra_require 包,所以当你在一个空的 venv 中有源代码并执行pipenv install --dev时,因为 Pipfile.lock 没有 extra_require 要求 pipenv仅在install_require上安装软件包。

安装程序.py

import os  # noqa: D100
from setuptools import setup, find_packages


def read(fname):
    """Read a file and return its content."""
    with open(os.path.join(os.path.dirname(__file__), fname)) as f:
        return f.read()


setup(
    name='auditor_client',
    version='0.0.0',
    description='Auditor client package',
    long_description=read('README.md'),
    packages=find_packages(exclude=['tests']),
    install_requires=['requests==2.9.1'],
    extras_require={'dev': ['flake8', 'flake8-docstrings', 'pytest', 'coverage', 'tox']},
    setup_requires=["pytest-runner"],
    tests_require=["pytest"]
)

点文件

[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[packages]
"e1839a8" = {editable = true, path = "."}

[requires]
python_version = "3.6"

[dev-packages]
"e1839a8" = {editable = true, extras = ["dev"], path = "."}

Pipfile.lock

{
    "_meta": {
        "hash": {
            "sha256": "e58b833e497814c83a2f0b93ad21d33a2af8b72721b20e9607a6c9135978422d"
        },
        "pipfile-spec": 6,
        "requires": {
            "python_version": "3.6"
        },
        "sources": [
            {
                "name": "pypi",
                "url": "https://pypi.org/simple",
                "verify_ssl": true
            }
        ]
    },
    "default": {
        "e1839a8": {
            "editable": true,
            "path": "."
        },
        "requests": {
            "hashes": [
                "sha256:113fbba5531a9e34945b7d36b33a084e8ba5d0664b703c81a7c572d91919a5b8",
                "sha256:c577815dd00f1394203fc44eb979724b098f88264a9ef898ee45b8e5e9cf587f"
            ],
            "version": "==2.9.1"
        }
    },
    "develop": {
        "e1839a8": {
            "editable": true,
            "path": "."
        },
        "requests": {
            "hashes": [
                "sha256:113fbba5531a9e34945b7d36b33a084e8ba5d0664b703c81a7c572d91919a5b8",
                "sha256:c577815dd00f1394203fc44eb979724b098f88264a9ef898ee45b8e5e9cf587f"
            ],
            "version": "==2.9.1"
        }
    }
}

不应该是 Pipfile.lock 跟踪 extra_require 开发包的正确行为吗?

是的,这对我来说似乎是一个错误/限制。 您应该为此提交单独的错误/问题。

我认为跟踪器中存在一个关于此问题的问题,尽管我现在无法找到它。 请在打开之前搜索现有问题。 提前致谢 :)

这不是错误,您不能在 pipfile 中多次使用相同的基本条目。 如果您在dev部分和默认部分中指定依赖项,则无论如何默认部分优先。

我将完成我正常的思想实验,但我现在没有时间,所以相信我的话,当你部署某些东西并发现你的开发依赖隐藏了冲突时,它可能会导致依赖冲突和意外。

@techalchemy那么在这种情况下如何管理我的开发依赖项? 我只想知道如何很好地使用 pipenv

我一直在为自己的项目考虑这个问题,并且有点意识到我真的不需要packages / dev-packages区别。 在packages中列出{editable = true, extras = ["dev"], path = "."}怎么样。

看看这个pipenv-setup 包

它将 pipfile/lockfile 同步到 setup.py

$ pipenv-setup sync

package e1839a8 is local, omitted in setup.py
setup.py successfully updated
23 packages from Pipfile.lock synced to setup.py

您可以使用$ pipenv-setup sync --dev将开发依赖项同步到额外的需求。 或$ pipenv-setup sync --pipfile改为同步 pipfile

$ pipenv-setup check只做检查

一个命令解决所有问题💯

有没有将 pipenv-setup 包合并到 pipenv 的计划?

@peterdeme

有没有将 pipenv-setup 包合并到 pipenv 的计划?

@uranusjr @techalchemy基于上面的讨论,我认为 pipenv 可能有一些不同的哲学。 但是如果维护者同意,我很想提交一个拉请求并尝试集成pipenv-setup

您始终可以使用内置的json模块解析Pipfile.lock 。 为您的setup.py install_requires提取non-dev依赖项。
"default"键包含包名称的嵌套“字典”以及version数字和markers
您不需要依赖任何外部导入。

@Kilo59我见过有人这样做。 需要提及的一个提示是不要忘记将 Pipfile.lock 作为 data_file 包含在 setup.py 中(或将其包含在 MANIFEST.in 中)。 这适用于具有固定依赖项的锁定文件。 另一方面,如果您想在 Pipfile 中进行语义版本控制,则 pipfile 解析起来并不简单。 相同的依赖要求可以以多种形式显示。

谢谢@Madoshakalaka,您的工具运行良好!

我同意其他同行的观点,即 Setup.py 的依赖项与 Pipfile 的项目依赖项不同。 但是,拥有一种可编程的方式来同步那些无需人工劳动的方式仍然是一个非常节省时间的功能。 此外,避免拼写错误/常见错误。

变黑的 setup.py 也是一个不错的选择:+1:

此页面是否有帮助?
0 / 5 - 0 等级