Python Poetry: 现代高效的Python环境和依赖管理工具

Python 的生态系统传统上依赖于像 pipvirtualenv 这样的工具来管理依赖和项目环境。虽然这些工具对我们很有用,但它们经常导致依赖冲突、手动环境管理以及团队之间项目设置的不一致。

Python Poetry 解决了这些挑战,提供了一个现代化、统一的依赖和环境管理工具。它处理从虚拟环境到包发布的所有事务,同时通过智能依赖解析确保可复制的构建。

本指南将向您展示如何使用Poetry来简化Python开发工作流程,避免常见的依赖问题。

Python Poetry vs. PIP

Poetry和pip在Python生态系统中有不同的用途。虽然pip主要是一个包安装程序,但Poetry是一个完整的依赖和项目管理工具。以下是它们之间的关键区别:

1. 依赖解析

  • Pip:简单的线性依赖解析可能导致冲突
  • 诗歌:高级依赖解析器,在安装前防止冲突

2. 虚拟环境管理

  • Pip:需要单独的工具(virtualenv, venv)和手动激活
  • Poetry:自动创建和管理每个项目的虚拟环境

3. 项目配置

  • Pip:使用 requirements.txt 来管理依赖,使用 setup.py 来管理项目元数据
  • 诗歌:单个pyproject.toml文件满足所有配置需求

4. 锁文件

  • Pip:没有内置的锁文件支持
  • Poetry:生成poetry.lock以实现跨环境的可重现构建

5. 包发布

  • Pip:需要额外的工具(twine、setuptools)来发布
  • Poetry:内置命令用于构建和发布包

何时使用 Poetry 而不是 pip

在以下情况下选择 Poetry:

  • 需要可复制的环境的团队项目
  • 构建将发布到 PyPI 的软件包
  • 管理具有潜在冲突的复杂依赖树
  • 需要自动化虚拟环境管理
  • 希望一个工具涵盖整个开发工作流程

使用 pip 时保持不变的情况:

  • 使用最少依赖项的简单脚本
  • 首次学习Python
  • 需要快速安装单个包
  • 在无法安装Poetry的环境中工作
  • 维护已经通过pip设置的遗留项目

一般经验法则是:对于任何将被共享、部署或长期维护的项目,请使用Poetry。对于快速实验或学习练习,请使用pip。

话虽如此,让我们马上开始使用Poetry。

设置Python Poetry

您将主要通过命令行(CLI)工具与Poetry互动,因此在您的计算机上系统范围内安装它是有意义的。本节介绍了这一关键的第一步以及如何根据您的需求设置Poetry的一些默认配置。

安装Poetry

您可以使用官方安装脚本安装Poetry,该脚本可下载并通过单个命令运行。

对于macOS、Linux和WSL2:

$ curl -sSL https://install.python-poetry.org | sudo python3 -

对于Windows Powershell(以管理员权限运行):

$ (Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -

如果您使用的是 Windows,并且从 Microsoft Store 安装了 Python(出于某种奇怪的原因),那么请在上述命令中将py替换为python

安装脚本完成后,会打印一条消息,要求您将Poetry添加到PATH中,以便在任何地方都可以使用poetry命令。

对于macOS、Linux和WSL2,请在您的shell脚本中添加以下行,例如.bashrc.zshrc

$ export PATH="/Users/bexgboost/.local/bin:$PATH"

对于 Windows,您可以按照输出的说明进行操作。

之后,通过运行 poetry --version 来验证您的安装。

配置 Poetry

Poetry 的大部分配置涉及虚拟环境的创建方式以及包的安装方式。您可以通过以下命令打印 (几乎) 完整的 Poetry 配置列表:

$ poetry config --list

输出将类似于以下内容:

cache-dir = "/Users/bexgboost/Library/Caches/pypoetry" experimental.system-git-client = false installer.max-workers = null installer.modern-installation = true installer.no-binary = null installer.parallel = true keyring.enabled = true solver.lazy-wheel = true virtualenvs.create = true virtualenvs.in-project = null virtualenvs.options.always-copy = false virtualenvs.options.no-pip = false virtualenvs.options.no-setuptools = false virtualenvs.options.system-site-packages = false virtualenvs.path = "{cache-dir}/virtualenvs" # /Users/bexgboost/Library/Caches/pypoetry/virtualenvs virtualenvs.prefer-active-python = false virtualenvs.prompt = "{project_name}-py{python_version}" warnings.export = true

在第一行中,我们看到了通往诗歌缓存的路径。这主要用于存储已下载的软件包分发和虚拟环境。您创建的任何虚拟环境都会默认存储在这里。如果您希望更改这一点,可以运行以下命令:

$ poetry config virtualenvs.path path/to/new/virtualenv/dir

另一个重要的配置是安装过程中使用的核心数。默认情况下,设置为四个,但我们可以通过利用所有 CPU 核心来加快速度。首先,通过运行os.cpu_count()在 Python 解释器中找出您的计算机核心数。然后,设置输出:

$ poetry config installer.max-workers = 10

一个可选的配置是在您的工作目录还是缓存中创建虚拟环境。这是通过virtualenvs.in-project 选项来控制。如果将其设置为True,则.venv 目录将始终在您的工作目录中创建:

$ poetry config virtualenvs.in-project true

使用Poetry创建新项目

让我们深入了解创建新Poetry项目以及了解其核心配置文件的关键步骤。

初始化一个新的Poetry项目

诗歌使用通常从使用poetry new命令创建新项目开始:

$ poetry new explore-poetry $ cd explore-poetry

该命令会创建一个explore-poetry目录,并预先填充了以下文件和目录:

explore-poetry ├── pyproject.toml ├── README.md ├── explore-poetry │ └── __init__.py └── tests └── __init__.py

该目录结构遵循Python最佳实践:

  • pyproject.toml:定义项目元数据和依赖项的主配置文件
  • README.md:解释项目的文档文件
  • explore-poetry/:包含主要软件包代码的源代码目录,带有__init__.py以使其成为一个包
  • tests/:测试文件目录,带有 __init__.py 以使其成为一个包(可导入)

了解 pyproject.toml

这里,pyproject.toml 文件需要特别注意,因为它是 Poetry 生成的唯一非空文件:

[tool.poetry] name = "explore-poetry" version = "0.1.0" description = "" authors = ["BexTuychiev <[email protected]>"] readme = "README.md" [tool.poetry.dependencies] python = "^3.8" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api"

这个文件协调您的项目及其依赖关系。它使用 Tom’s Obvious, Minimal Language,这是在 PEP 518中被确定为Python软件包的标准配置语言。

所有pyproject.toml文件被划分为称为的部分,使用方括号如tool.poetrybuild-system。Poetry使用这些表来管理依赖关系、项目构建需求或执行其他任务。

在运行接下来部分中概述的Poetry命令时,pyproject.toml文件将被自动更新。

在Poetry中使用虚拟环境

在本节中,您将了解设置Poetry后如何管理虚拟环境的方方面面。您将了解虚拟环境的重要性,Poetry如何自动处理它们,以及用于操控虚拟环境的基本命令,如创建、激活和切换。您还将学习如何使用Poetry管理Python版本。

创建和激活虚拟环境

当您第一次安装Poetry时,它不会自带任何内置环境,这可以通过运行poetry env list时输出为空:

$ poetry env list # 没有输出

但是一旦您开始使用 poetry add package-name 添加依赖项(稍后会详细介绍),它会自动在缓存目录为项目创建一个环境。例如,让我们尝试添加 requests 作为一个依赖项:

$ poetry add requests

您应该会收到类似以下内容的输出:

Creating virtualenv explore-poetry--I9GJYUn-py3.8 in /Users/bexgboost/Library/Caches/pypoetry/virtualenvs Using version ^2.32.3 for requests Updating dependencies Resolving dependencies... (2.5s) Package operations: 5 installs, 0 updates, 0 removals - Installing certifi (2024.8.30) - Installing charset-normalizer (3.4.0) - Installing idna (3.10) - Installing urllib3 (2.2.3) - Installing requests (2.32.3) Writing lock file

第一行表示诗歌在缓存中创建了环境。接下来的几行通知依赖项 请求 已成功解决,并生成了一个锁定文件(稍后会详细介绍)。

注意环境名称:explore-poetry--I9GJYUn-py3.8。该名称包括项目目录名称,后跟唯一ID,然后是环境正在使用的Python版本。

下次您添加一个依赖项时,Poetry将自动使用此环境来安装软件包:

$ poetry add beautifulsoup4 Using version ^4.12.3 for beautifulsoup4 Updating dependencies Resolving dependencies... (1.1s) Package operations: 2 installs, 0 updates, 0 removals - Installing soupsieve (2.6) - Installing beautifulsoup4 (4.12.3) Writing lock file

使用Poetry时,您的终端不会显示哪个Poetry虚拟环境处于活动状态。要查看此信息,您需要运行poetry env list

$ poetry env list explore-poetry--I9GJYUn-py3.8 (Activated)

要与活动的Poetry环境进行交互,您可以运行poetry shell

$ poetry shell

该命令在当前会话中打开一个新的 shell 会话,您可以执行类似于pythonpytest等命令。

例如,要执行 Python 脚本:

$ poetry shell $ python script.py

如需运行像 Streamlit 这样的开发框架:

# 在 shell 中 $ streamlit run app.py

之后,您可以通过调用exit退出shell。

另外,您可以在不进入Poetry shell的情况下运行命令,使用poetry run

$ poetry run python script.py $ poetry run streamlit run app.py

配置Python版本

当您运行poetry addpoetry install时,Poetry会自动使用您pyproject.toml文件中指定的Python版本。要指定不同的Python版本,您可以运行env use命令:

$ poetry env use python3.11

请注意,必须在您的计算机上系统范围内安装Python 3.11 才能使命令正常工作。

检查Poetry是否正在使用具有新Python版本的环境:

$ poetry env list explore-poetry--I9GJYUn-py3.11 (Activated) explore-poetry--I9GJYUn-py3.8

注意新环境如何自动附加到我们的项目(ID相同)。配置Python版本后,您可以删除具有其他版本的环境以释放磁盘空间:

$ poetry env remove python3.8

删除环境时,只需指定Python版本即可。您也可以删除所有环境,从头开始:

$ poetry env remove --all

请注意,--all 标签会删除当前项目关联的所有环境,而不会影响其他项目的环境。

如果您正在进行团队项目,通常最好将虚拟环境保留在项目目录中:

$ poetry config virtualenvs.in-project true

如果缓存中已经存在该项目的环境,则此命令不会产生任何效果。要创建本地环境,请先从缓存中删除所有现有环境。

这将在您的工作目录中创建一个.venv文件夹,您应该将其添加到您的.gitignore文件中。

使用 Poetry 管理依赖项

依赖项管理是 Poetry 的亮点。它提供了全面的功能来指定、安装和管理依赖项,确保您永远不会遇到糟糕的依赖冲突。

在这一部分,您将学习如何添加和安装依赖项,创建依赖组,依赖项规范语法在pyproject.toml和锁定文件中。

在Poetry中添加依赖项

在使用Poetry时,您将使用poetry add package-name命令来安装PyPI中的软件包,而不是pip install。这带来了一些好处:

  1. 自动将软件包添加到pyproject.toml中,并附带正确的版本约束
  2. 更新锁文件以确保可复现的构建
  3. 解决依赖关系以避免冲突
  4. 在虚拟环境中安装软件包及其所有依赖项

例如,让我们首先将Numpy添加为一个依赖项:

$ poetry add numpy

立即,您应该收到一个依赖项解决冲突的消息,说明正在安装的Numpy版本与您的Python版本不兼容。原因是我们切换到Python 3.11环境时,没有更新pyproject.toml 文件。现在,它看起来像这样:

[tool.poetry.dependencies] python = "^3.8" requests = "^2.32.3" beautifulsoup4 = "^4.12.3"

插入符号 ^ 用于表示我们的 explore-poetry 项目兼容任何 Python 版本直到 Python 4,但 Numpy 的版本范围仅支持在 3.8 到 3.12 之间的 Python 版本,这是一个较窄的范围。冲突错误就是由此造成的。

因此,要解决错误,您应将 Python 版本范围更新为以下内容:

python = ">=3.8, <3.12"

一旦您进行了这个更改,poetry add numpy 命令应该可以正常工作。

您刚刚体验到了 Poetry 最好的功能之一 — 在安装任何软件包之前捕获版本冲突,而不是像 pip 那样事后通知您版本不兼容。

指定依赖版本的语法

诗歌使用强大的语法来指定最佳依赖项之间的广泛版本同步。以下是最常用的符号:

  1. 插入符(^):允许补丁和次要更新,但不允许主要更新。示例:^1.2.3 允许从1.2.3更新到1.9.9,但不包括2.0.0。
  2. 波浪号(~):仅允许补丁更新。示例:~1.2.3 允许从1.2.3更新到1.2.9,但不包括1.3.0。
  3. 精确版本:指定精确版本号。示例:1.2.3 仅允许版本号1.2.3
  4. 大于 (>):允许任何高于指定版本的版本。示例:>1.2.3 允许1.2.4, 1.3.0, 2.0.0等。
  5. 小于 (<): 允许任何指定版本以下。示例: <2.0.0 允许任何低于 2.0.0 的版本。
  6. 大于或等于 (>=): 允许指定版本及以上。示例: >=1.2.3 允许版本 1.2.3 及更高版本。
  7. 小于或等于(<=):允许指定版本及以下。示例:<=2.0.0 允许2.0.0及任何更低版本。
  8. 版本范围:使用逗号结合约束。示例:>=1.2.3,<2.0.0 允许版本从1.2.3到1.9.9
  9. 通配符 (*): 匹配任何版本。示例: 1.2.* 匹配以1.2开头的任何版本

当然,您可以混合匹配这些以满足您的需求。

在Poetry中创建依赖关系组

在具有许多移动组件的复杂项目中,您通常会处理不同类别的依赖项。

例如,在机器学习项目中,您经常会为数据摄取、数据清洗、特征工程、模型训练、部署和监控创建不同的流水线和组件。更不用说,您还需要完成文档编写和运行测试。所有这些步骤都有其工具生态系统,并且混合它们的依赖会使您的最终分发包膨胀。

使用Poetry,您可以创建具有任意名称的依赖组,以便只在需要时安装它们。或者最棒的部分是,您的软件包的用户也可以选择安装他们需要的部分。

$ poetry add --group ui streamlit plotly dash $ poetry add --group dev black flake8 isort mypy pylint

上述命令创建两个依赖组,uidev(如果它们尚不存在),以及两个子表在 pyproject.toml

[tool.poetry.dependencies] python = "^3.11" requests = "^2.32.3" beautifulsoup4 = "^4.12.3" numpy = "^2.1.3" [tool.poetry.group.ui.dependencies] streamlit = "^1.39.0" plotly = "^5.24.1" dash = "^2.18.2" [tool.poetry.group.dev.dependencies] black = "^24.10.0" flake8 = "^7.1.1" isort = "^5.13.2" mypy = "^1.13.0" pylint = "^3.3.1"

尽管依赖组是分开的,但它们仍然会相互解析。换句话说,如果dev依赖与ui中的一个软件包发生冲突,Poetry 将无法运行安装。

在某些情况下,您可以创建可选的依赖组,或使现有组成为可选项,这样当用户重新创建您的项目环境时,默认情况下不会安装它们。要实现这一点,您需要在 pyproject.toml 文件中创建一个新的子表。例如,我们将使 ui 组成为可选项:

[tool.poetry.group.ui] optional = true [tool.poetry.group.ui.dependencies] streamlit = "^1.39.0" plotly = "^5.24.1" dash = "^2.18.2"

通过将optional参数设置为true,然后声明组的依赖项。

使用 poetry.lock 文件复制一个 Poetry 项目

如果有人克隆您的 GitHub 存储库,其中包含一个 Poetry 项目,他们可以通过运行一个命令来重新创建项目的虚拟环境的完美副本:poetry install

install命令使用更全面的poetry.lock文件。不像pyproject.toml,锁定文件列出:

  • 主要依赖项的确切版本列在pyproject.toml
  • 主要依赖项的依赖项的确切版本(传递依赖项)

例如,requests 依赖于 urllib3, certifi, charset-normalizeridna。没有锁文件,这些子依赖项可能在不同机器上解析为不同版本。

锁定文件确保团队中的每个人都获得相同的依赖版本,避免了“在我的机器上可以运行”的问题。

poetry add 和 poetry install之间的区别

我们将解释 addinstall 命令在Poetry中的用法案例。

假设您正在进行一个欺诈检测项目。您添加了初始依赖项:

$ poetry add pandas scikit-learn

这将添加软件包到锁定和pyproject.toml文件中。然后,您的同事克隆存储库:

$ git clone fraud-detection-repo-link $ cd fraud-detection $ poetry install

他们运行install命令来安装锁定文件中列出的所有内容。

稍后,您需要添加一个新软件包:

$ poetry add xgboost

您的同事拉取更改:

$ git pull $ poetry install

他们运行 install 命令来安装新包。所以,

  • 在添加新依赖项时,请使用 poetry add
  • 在需要设置现有项目时使用poetry install命令
  • 始终将pyproject.tomlpoetry.lock提交至版本控制

使用poetry install命令安装依赖组

早些时候,我们学习了如何在Poetry中对依赖项进行分组。当你运行poetry install时,默认情况下会安装所有非可选组,这可能并非在所有情况下都是你想要的。

例如,你可能只是为了处理文档而克隆一个存储库。或者你想处理存储库的主要代码,但不包括文档和测试方面。 install命令足够灵活,可以涵盖所有这些情况:

排除某些组:

$ poetry install --without ui,dev

安装可选组:

# 安装可选文档组 $ poetry install --with docs

仅安装特定组:

$ poetry install --only ui

仅安装项目的运行时依赖项(在组外提到的依赖项,使用普通 poetry add package 命令添加):

$ poetry install --only main

在Poetry中移除依赖项

删除依赖项很简单,使用remove命令:

$ poetry remove requests

这将从主项目依赖项中移除requests。要从组依赖项中移除一个包,您可以再次使用--group标记:

$ poetry remove streamlit --group ui

remove命令可以干净地卸载包,以及其传递依赖关系。

使用Poetry将项目发布到PyPI

如果您的项目已准备好进行分发,将其发布到PyPI(Python包索引)可以让其他开发人员通过pip轻松安装和使用您的代码。Poetry通过只需两个命令使这一过程变得非常简单:

$ poetry build # 构建发布包 $ poetry publish # 发布到PyPI

但在运行这些命令之前,您应该正确设置您的PyPI凭据。首先,在以下网址创建您的帐户:

  1. PyPI
  2. TestPyPI

使用您的凭据配置Poetry:

$ poetry config pypi-token.pypi your-pypi-token $ poetry config pypi-token.testpypi your-test-pypi-token

现在,首先测试您的软件包:

# 配置TestPyPI仓库 $ poetry config repositories.testpypi https://test.pypi.org/legacy/ # 发布到TestPyPI $ poetry build $ poetry publish -r testpypi

在发布到 TestPyPI 后,您可以尝试安装您的包来测试一切是否正常:

$ pip install --index-url https://test.pypi.org/simple/ your-package-name

如果一切正常,您可以发布到 PyPI 本身:

$ poetry publish

使用 Poetry 时的最佳实践

在使用 Poetry 时有许多需要注意的地方和最佳实践,当然,我们无法在一篇文章中全部提及。但这里有一些常见的实践建议您可以立即应用:

  1. 始终使用虚拟环境 – Poetry 会自动为每个项目创建虚拟环境
  2. 将您的pyproject.toml文件放入版本控制,但排除poetry.lock用于库
  3. 对于应用程序,请在版本控制中包含poetry.lock以确保可重现的构建
  4. 使用语义化版本号来管理您的包版本(主版本号.次版本号.补丁版本号)。您可以使用poetry version patch/minor/major命令来递增包版本号。例如,poetry version major0.2.0更改为1.0.0在您的pyproject.toml文件中。
  5. 仔细指定依赖版本约束,以避免冲突
  6. 定期使用 poetry update 更新依赖,但更新后要进行彻底测试
  7. 使用 poetry add --group dev 来添加开发依赖,以使它们保持分离
  8. 使用注释在pyproject.toml中记录所有依赖项的目的
  9. 在提交之前运行poetry check以验证pyproject.toml的语法。您还可以考虑 pre-commit hooks
  10. 使用 poetry export 命令生成 requirements.txt 文件,以供其他工具使用时需要。
  11. 保持生产依赖项的最小化 — 将可选功能移到额外内容中
  12. 在发布之前在干净的环境中测试您的软件包安装
  13. 在发布到主 PyPI 仓库之前,请使用 TestPyPI
  14. 保持清晰的 CHANGELOG.md 以跟踪版本更改
  15. 使用 poetry run 来执行脚本以确保正确的环境使用

结论和下一步操作

Poetry通过提供强大直观的解决方案,彻底改变了Python依赖管理,解决了常见的软件包管理挑战。其强大的依赖解析、虚拟环境管理和项目发布功能,使其成为现代Python开发中不可或缺的工具。

要继续你的Python开发之旅,请考虑探索以下全面的学习路径:

记住,有效的依赖管理只是专业 Python 开发的一个方面。随着您作为开发人员的成长,诸如 Poetry 这样的工具将帮助您构建更易维护和可靠的 Python 项目。

Source:
https://www.datacamp.com/tutorial/python-poetry