Python 的生态系统传统上依赖于像 pip
和 virtualenv
这样的工具来管理依赖和项目环境。虽然这些工具对我们很有用,但它们经常导致依赖冲突、手动环境管理以及团队之间项目设置的不一致。
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.poetry
或build-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 会话,您可以执行类似于python
或pytest
等命令。
例如,要执行 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 add
或poetry 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
。这带来了一些好处:
- 自动将软件包添加到
pyproject.toml
中,并附带正确的版本约束 - 更新锁文件以确保可复现的构建
- 解决依赖关系以避免冲突
- 在虚拟环境中安装软件包及其所有依赖项
例如,让我们首先将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.2.3
允许从1.2.3更新到1.9.9,但不包括2.0.0。 - 波浪号(~):仅允许补丁更新。示例:
~1.2.3
允许从1.2.3更新到1.2.9,但不包括1.3.0。 - 精确版本:指定精确版本号。示例:
1.2.3
仅允许版本号1.2.3 - 大于 (>):允许任何高于指定版本的版本。示例:
>1.2.3
允许1.2.4, 1.3.0, 2.0.0等。 - 小于 (<): 允许任何指定版本以下。示例:
<2.0.0
允许任何低于 2.0.0 的版本。 - 大于或等于 (>=): 允许指定版本及以上。示例:
>=1.2.3
允许版本 1.2.3 及更高版本。 - 小于或等于(<=):允许指定版本及以下。示例:
<=2.0.0
允许2.0.0及任何更低版本。 - 版本范围:使用逗号结合约束。示例:
>=1.2.3,<2.0.0
允许版本从1.2.3到1.9.9 - 通配符 (*): 匹配任何版本。示例:
1.2.*
匹配以1.2开头的任何版本
当然,您可以混合匹配这些以满足您的需求。
在Poetry中创建依赖关系组
在具有许多移动组件的复杂项目中,您通常会处理不同类别的依赖项。
例如,在机器学习项目中,您经常会为数据摄取、数据清洗、特征工程、模型训练、部署和监控创建不同的流水线和组件。更不用说,您还需要完成文档编写和运行测试。所有这些步骤都有其工具生态系统,并且混合它们的依赖会使您的最终分发包膨胀。
使用Poetry,您可以创建具有任意名称的依赖组,以便只在需要时安装它们。或者最棒的部分是,您的软件包的用户也可以选择安装他们需要的部分。
$ poetry add --group ui streamlit plotly dash $ poetry add --group dev black flake8 isort mypy pylint
上述命令创建两个依赖组,ui
和 dev
(如果它们尚不存在),以及两个子表在 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-normalizer
和 idna
。没有锁文件,这些子依赖项可能在不同机器上解析为不同版本。
锁定文件确保团队中的每个人都获得相同的依赖版本,避免了“在我的机器上可以运行”的问题。
poetry add 和 poetry install之间的区别
我们将解释 add
和 install
命令在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.toml
和poetry.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凭据。首先,在以下网址创建您的帐户:
使用您的凭据配置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 时有许多需要注意的地方和最佳实践,当然,我们无法在一篇文章中全部提及。但这里有一些常见的实践建议您可以立即应用:
- 始终使用虚拟环境 – Poetry 会自动为每个项目创建虚拟环境
- 将您的
pyproject.toml
文件放入版本控制,但排除poetry.lock
用于库 - 对于应用程序,请在版本控制中包含
poetry.lock
以确保可重现的构建 - 使用语义化版本号来管理您的包版本(主版本号.次版本号.补丁版本号)。您可以使用
poetry version patch/minor/major
命令来递增包版本号。例如,poetry version major
将0.2.0更改为1.0.0在您的pyproject.toml
文件中。 - 仔细指定依赖版本约束,以避免冲突
- 定期使用
poetry update
更新依赖,但更新后要进行彻底测试 - 使用
poetry add --group dev
来添加开发依赖,以使它们保持分离 - 使用注释在pyproject.toml中记录所有依赖项的目的
- 在提交之前运行
poetry check
以验证pyproject.toml的语法。您还可以考虑 pre-commit hooks。 - 使用
poetry export
命令生成requirements.txt
文件,以供其他工具使用时需要。 - 保持生产依赖项的最小化 — 将可选功能移到额外内容中
- 在发布之前在干净的环境中测试您的软件包安装
- 在发布到主 PyPI 仓库之前,请使用 TestPyPI
- 保持清晰的
CHANGELOG.md
以跟踪版本更改 - 使用
poetry run
来执行脚本以确保正确的环境使用
结论和下一步操作
Poetry通过提供强大直观的解决方案,彻底改变了Python依赖管理,解决了常见的软件包管理挑战。其强大的依赖解析、虚拟环境管理和项目发布功能,使其成为现代Python开发中不可或缺的工具。
要继续你的Python开发之旅,请考虑探索以下全面的学习路径:
- Python编程专项 – 掌握Python基础和高级概念
- 中级Python — 提升你的Python技能
- Python数据基础 — 学习基本数据操作技能
- Python 数据科学家助理 – 用 Python 深入数据科学
记住,有效的依赖管理只是专业 Python 开发的一个方面。随着您作为开发人员的成长,诸如 Poetry 这样的工具将帮助您构建更易维护和可靠的 Python 项目。