发布于 2026-01-06 6 阅读
0

评测:Pipenv、Poetry 和 PDM 的抽象设置、性能、正确性及结论

评测:Pipenv vs. Poetry vs. PDM

抽象的

设置

表现

正确性

结论

抽象的

现在是 2021 年,我们都在使用或听说过 Python 中的包管理器,例如 Pipenv 和 Poetry。我也开发了一个新的包管理器PDM来解决类似的问题。社区里对它们有一些比较,但本文不会讨论用户界面或功能多样性,而是重点关注两个重要方面:性能和正确性。

设置

PipenvHEAD@275f7e151eb0aa17702215165a371df7da9ad476

诗歌1.1.5

PDMHEAD@d72ba5d2ee0b0305f917d8739667ba78465c5cc8

Python 版本3.9.1

表现

依存关系集(诗歌格式):

[tool.poetry.dependencies]
python = "^3.9"
requests = { git = "https://github.com/psf/requests.git", tag = "v2.25.0" }
numpy = "^1.19"

[tool.poetry.dev-dependencies]
pytest = "^5.2"
Enter fullscreen mode Exit fullscreen mode

这包括对 Git 的依赖以及对一个大型二进制文件的依赖。以下结果中,耗时以秒为单位。

除非另有明确规定,否则测量将按照install命令进行。

结果

Pipenv PDM
清除缓存,无锁定文件 98 150 58
启用缓存,无需锁定文件 117 66 28
清除缓存,重用锁定文件* 128 145 35
使用缓存,重用锁定文件** 145 50 16

*:分别使用命令进行测试poetry add click pipenv install --keep-outdated click pdm add click

**:命令与上述相同。

绩效考核

  • Pipenv的缓存系统存在问题,缓存的存在会降低性能。
  • 诗歌和 PDM 都从缓存中受益匪浅,PDM 甚至花费的时间更少。
  • Pipenv 使用了一种截然不同的机制来重用锁定文件——它先执行完整的锁定操作,然后再修改旧锁定文件的内容,而 PDM 可以直接重用锁定文件中已锁定的版本。Poetry 在锁定文件存在的情况下性能略有提升。

正确性

这三个包管理器的目标都是构建一个可复现的环境,它们都尽力使其能够在跨平台和不同 Python 版本上运行。让我们看看结果如何。

Python兼容性

项目文件设计如下(诗歌):

[tool.poetry.dependencies]
python = "^3.6||^2.7"
Enter fullscreen mode Exit fullscreen mode

以及PDM:

[project]
requires-python = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*"
Enter fullscreen mode Exit fullscreen mode

它们都支持相同的 Python 版本范围。虽然 Pipenv 不支持 Python 版本范围限制,但我也会在这里贴出它的测试结果以供参考。

现在让我们pytest从一个全新的项目中添加一个依赖项。之所以特意选择这个依赖项,是因为它的版本设置不同,可以同时支持 Python 2 和 Python 3。

诗歌的结果:

$ poetry add pytest
...
Using version ^6.2.2 for pytest

Updating dependencies
Resolving dependencies...

  SolverProblemError

  The current project's Python requirement (>=2.7,<3.0 || >=3.6,<4.0) is not compatible with some of the required packages Python requirement:
    - pytest requires Python >=3.6, so it will not be satisfied for Python >=2.7,<3.0

  Because no versions of pytest match >6.2.2,<7.0.0
   and pytest (6.2.2) requires Python >=3.6, pytest is forbidden.
  So, because foo depends on pytest (^6.2.2), version solving failed.
Enter fullscreen mode Exit fullscreen mode

锁被6.2.2撬开了,但与 Python 2.7 不兼容。无论如何,Poetry 会显示一条易于理解的错误信息,告诉人们发生了什么以及如何解决。

Pipenv 的结果

根据创建虚拟环境时使用的是 Python 3 还是 Python 2(带--three/--two选项),分别pytest将其锁定到6.2.2相应4.6.11的 Python 环境。锁定文件显然无法同时在 Python 2 和 Python 3 环境中工作。

PDM结果

$ pdm add pytest
Adding packages to default dependencies: pytest
✔ 🔒 Lock successful
...

  ✔ Install pytest 4.6.11 successful

...
🎉 All complete!
Enter fullscreen mode Exit fullscreen mode

pytest已正确解析为4.6.11支持requires-python约束内的所有版本。

版本树搜索

当当前依赖项解析器导致版本冲突时,它应该能够搜索其他候选依赖项。

让我们以Poetry 的 README 文件中的例子为例

Pipenv 的结果

$ pipenv install oslo.utils==1.4.0
...
There are incompatible versions in the resolved dependencies:
  pbr!=0.7,<1.0,>=0.6 (from oslo.utils==1.4.0->-r C:\Users\FROSTM~1\AppData\Local\Temp\pipenvkbeeio2trequirements\pipenv-0zsj0laj-constraints.txt (line 3))
  pbr!=2.1.0,>=2.0.0 (from oslo.i18n==5.0.1->oslo.utils==1.4.0->-r C:\Users\FROSTM~1\AppData\Local\Temp\pipenvkbeeio2trequirements\pipenv-0zsj0laj-constraints.txt (line 3))
Enter fullscreen mode Exit fullscreen mode

oslo.i18n由于 Pipenv 未能搜索到与当前版本兼容的较低版本,因此无法解析*pbr<1.0

*:请注意,Pipenv 的策略是“安装后锁定”,因此在报告锁定失败之前,不兼容的软件包将被安装到环境中。

诗歌的结果

如 README 文件中所示,诗歌可以成功地解决冲突oslo.i18n==2.1.0。它会在候选列表中搜索oslo.i18n并丢弃那些会带来冲突的词。

PDM结果

$ pdm add oslo.utils==1.4.0
...
  ✔ Install oslo.i18n 2.1.0 successful
...
Enter fullscreen mode Exit fullscreen mode

同样,PDM 也能使用相同版本的软件成功锁定。oslo.i18n

环境标记传播

为了创建跨平台项目模板,我们通常需要定义一些平台特定的依赖项,而这些依赖项可能还有子依赖项。这些平台特定的依赖项可能无法在源系统上成功构建。我们不希望将这些依赖项及其子依赖项安装在不符合要求的系统上。

我们先来添加一个依赖项gevent; os_name == "posix",它还有几个子依赖项。命令是在Windows计算机上运行的。

Pipenv(Pipfile.lock) 的结果

{
    "_meta": {
        "hash": {
            "sha256": "9ce84144d1fc47c173581ae74c51ffbe28ac242b1fe0e1642915e803f15d3063"
        },
        "pipfile-spec": 6,
        "requires": {},
        "sources": [
            {
                "name": "pypi",
                "url": "https://pypi.org/simple",
                "verify_ssl": true
            }
        ]
    },
    "default": {
        "gevent": {
            "hashes": [
                ...
            ],
            "markers": "os_name == 'posix'",
            "version": "==21.1.2"
        }
    },
    "develop": {}
}
Enter fullscreen mode Exit fullscreen mode

只有gevent当锁文件中的标记与当前系统不匹配时,Pipenv 才会停止查找其子依赖项。这意味着,如果您使用此 Pipfile.lock 文件在目标 Linux 服务器上部署,某些重要的依赖项将不会被安装!

诗歌的结果

geventgreenlet`<dependencies>`、`<dependencies>` cffi、 `<dependencies>`、`<dependencies> `一起被锁定在锁定文件中,不会安装到环境中,这是预期行为。但我没弄明白如何通过命令行添加带有标记的依赖项,只能手动写入。此外,如果我之后运行 `<dependencies>`,`<dependencies>`就可以正确安装。pycparserzope.eventzope.interfacepyproject.tomlpoetry add pycparserpycparser

PDM结果

与 Poetry 的结果相同,只是在 中pdm.lock,子依赖项也有标记os_name == 'nt',这样安装程序就不必搜索依赖关系树来确定是否应该安装单个软件包。

结论

从性能角度来看,Pipenv 的表现并不理想,这主要是因为它采用了集成其他第三方工具和库的设计方式,而不是自行构建。Pipenv 只能对这些上游库进行封装、组合和少量改进。此外,Pipenv 也无法满足可复现环境的目标。虽然它可以在源系统上生成确定性的安装配置,但如果没有仔细检查就将其部署到其他系统上,则并非明智之举。

相比之下,Poetry 和 PDM 在性能和正确性方面都表现出色,PDM 尤其在时间成本和兼容性依赖解决方面更胜一筹。如果您还不了解这款工具,现在就开始吧

文章来源:https://dev.to/frostming/a-review-pipenv-vs-poetry-vs-pdm-39b4