Python项目管理工具:Poetry 使用指南
在项目开发中,良好的代码管理工具至关重要。对于 Python 开发而言,核心诉求是管理项目依赖、虚拟环境、项目元信息等。近年来,一系列 PEP(Python 增强提案)规范了使用 pyproject.toml 文件进行项目依赖、构建和元信息管理。伴随着标准落地,许多新工具渐次发布,首当其冲的就是 Poetry。Poetry 是用于 Python 的依赖管理和打包工具,设计目标是简化和提升 Python 包的创建、管理与发布过程。在 Poetry 官网 中对其的介绍是:Poetry 是让 Python 打包和依赖管理更加轻松的工具。
本文将通过实际操作的方式,一步步了解 Poetry 的功能与常见工作流。本文基于以下版本进行演示:
OS:Debian 13(Trixie)
Python:3.13
poetry:2.3.2
Poetry 还在快速更新迭代中,有些配置和用法可能会因版本差异而不同,请注意区分。
安装和使用
安装 Poetry 的方法见官方文档: https://python-poetry.org/docs。不同平台有各自的安装方法,建议使用 pipx 安装。
$ pipx install poetry
$ poetry --version
Poetry (version 2.3.2)
Poetry 版本升级方法:
$ pipx upgrade poetry
Poetry 常用命令速览:
$ poetry new
$ poetry init
$ poetry add
$ poetry remove
$ poetry install
$ poetry sync
$ poetry run
项目管理
以下命令可以用于了解 Poetry 的用法和版本信息等。
$ poetry list
$ poetry about
子命令介绍:
+ list 查看所有 Poetry 支持的子命令。
+ about 查看 Poetry 的版本等信息。
Poetry 有一个全局性的 --verbose 选项,或者 -v/-vv/-vvv,可以用来观察 Poetry 的 debug 信息和具体的运行过程。
Poetry 的全局配置
查看 Poetry 的默认配置:
$ poetry config --list
cache-dir = "~/.cache/pypoetry" # 缓存目录
data-dir = "~/.local/share/pypoetry"
installer.max-workers = null
virtualenvs.path = "{cache-dir}/virtualenvs" # 项目虚拟环境目录
...
设置 Poetry 的配置项。修改 Poetry 的全局配置会影响 Poetry 的行为,谨慎修改。
$ poetry config virtualenvs.in-project true
$ poetry config repositories.aliyun https://mirrors.aliyun.com/pypi/simple/
- 修改
virtualenvs.in-project配置项为true。默认情况下,Poetry 会在virtualenvs.path配置规定的目录下创建项目的虚拟环境,修改该项为true后,Poetry 会在项目目录下创建虚拟环境,目录为.venv。 - 配置一个名为 aliyun 的 PyPI 源,修改为国内源以提升依赖安装速度。
Poetry 的配置保存在 ~/.config/pypoetry/config.toml 文件中。
$ cat ~/.config/pypoetry/config.toml
[virtualenvs]
in-project = true
[repositories.aliyun]
url = "https://mirrors.aliyun.com/pypi/simple/"
以上是比较常见的 Poetry 全局指令。接下来是和项目管理相关的内容。
创建新项目
使用 Poetry 的 new 子指令创建一个全新的项目。
$ poetry new myproj
Created package myproj in myproj
$ tree myproj
myproj
├── pyproject.toml
├── README.md
├── src
│ └── myproj
│ └── __init__.py
└── tests
└── __init__.py
new 子命令创建新的 Python 项目。新项目目录包含基础的项目结构,除 pyproject.toml 外还会生成 README.md、tests 等基础文件,内容多为占位信息。
Python 项目有两种常见结构:一种是把包放在项目的顶级目录下,一般称为 flat 布局,许多 Web 项目都是这么做的;另一种是把包放到项目的 src 目录下,称为 src 布局,许多库项目采取了这种方式。Poetry 默认采用 src 布局,如果想使用 flat 布局,可以传入 --flat 参数。
$ poetry new myproj2 --name app --flat
Created package app in myproj2
$ tree myproj2
myproj2
├── app
│ └── __init__.py
├── pyproject.toml
├── README.md
└── tests
└── __init__.py
--name参数用于指定创建项目的包名,如果不指定,包名和项目名一样。--flat指定创建 flat 布局项目,包名 app 就在项目顶级目录下,没有 src 目录了。生成的pyproject.toml文件也有细微区别。
初始化已有项目
如果现有项目要切换到 Poetry 进行管理,可以使用 init 指令,会在现有项目中创建 pyproject.toml 文件。
$ mkdir myproj3
$ cd myproj3
$ poetry init --no-interaction # 不使用交互式输入项目信息
$ ls
pyproject.toml # 仅仅创建了 pyproject.toml 文件
Poetry 初始化项目的工作十分简单,只新增了一个 pyproject.toml 文件。如果 init 指令不使用 --no-interaction 参数,Poetry 会出现交互式配置提示,需要一步步填入项目元信息。
check 子命令用于检查 pyproject.toml 文件的格式是否正确。
$ poetry check
All set!
项目配置
Poetry 通过 pyproject.toml 文件实现大部分管理功能,部分 Poetry 子命令(比如 add、remove 等)会修改这个文件,这个文件也可以手动修改。
pyproject.toml并不是 Poetry 的发明,PEP 518 定义了该文件作为构建配置入口,PEP 621 定义了[project]元信息规范。其他符合 PEP 规范的工具也可以使用这个文件。
以下是 myproj2 项目的 pyproject.toml 文件
$ cd myproj2
$ cat pyproject.toml
[project]
name = "app"
version = "0.1.0"
description = ""
authors = [
{name = "Your Name",email = "you@example.com"}
]
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
]
[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"
在 TOML 格式中,方括号代表 table,类似一个子 JSON,其中的等号定义内容相当于 JSON 中的键值对。
[project]定义了项目的元信息,例如 name、version、description、authors 等。requires-python指定本项目依赖的 Python 版本。dependencies用于指定本项目的依赖。[build-system]中指定了库项目构建相关的依赖和构建后端。使用 Poetry 打包/发布时必须保留该部分;即便是应用项目,保留默认配置也更稳妥。
PyPI 源管理
在 Poetry 管理的项目中,可以使用 source 子命令管理 PyPI 源,source 子命令下有 add、show、remove 等子命令用于对 PyPI 源进行管理。
$ poetry source add aliyun https://mirrors.aliyun.com/pypi/simple
$ poetry source add douban https://pypi.doubanio.com/simple
$ poetry source show
name : aliyun
url : https://mirrors.aliyun.com/pypi/simple
priority : primary
name : douban
url : https://pypi.doubanio.com/simple
priority : primary
aliyun 和 douban 两个源的优先级都是 primary,当存在多个 primary 源时,Poetry 会按它们在 pyproject.toml 中的顺序进行查找;未显式指定 --source 的依赖通常会优先走第一个 primary 源。
如果需要从特定的 source 安装依赖,可以用 --source 指定源站。
$ poetry add flask --source douban
在运行上面的命令之后,目录下会产生 poetry.lock 文件,它非常重要,记录了当前项目的依赖关系和依赖版本等信息,相当于一个快照,poetry sync 等命令会读取 poetry.lock 还原出完全一致的依赖环境。
再次查看 pyproject.toml 文件,重点看看其中的依赖部分:
[project]
...
requires-python = ">=3.13"
dependencies = [
"flask (>=3.1.3,<4.0.0)"
]
[[tool.poetry.source]]
name = "aliyun"
url = "https://mirrors.aliyun.com/pypi/simple"
priority = "primary"
[[tool.poetry.source]]
name = "douban"
url = "https://pypi.doubanio.com/simple"
priority = "primary"
[tool.poetry.dependencies]
flask = {source = "douban"}
project.dependencies中指明了项目依赖的 flask 版本。[tool.poetry.dependencies]中指明了 Flask 依赖来自的源。这在某些场景很有用,比如英伟达的 CUDA 源分为 CPU 版本和 GPU 版本,CPU 就要使用特定的源才能安装。
非 package 项目
Poetry 项目分两种模式:
默认是
package模式:意味项目需要被打包和发布,这种模式下pyproject.toml中的version是强制必填字段,运行poetry install会安装当前项目。一般采用 src 项目布局。non-package模式:意味这是应用类型的项目。一般使用 flat 项目布局。
在 [tool.poetry] 中添加如下配置,指定为 non-package 模式。
package-mode = false
一般来说,用什么模式的区别不大,项目的布局更重要一些。不管是不是 package 模式,都建议设置 version 字段。
poetry run
使用 poetry run 会在当前的 Poetry 环境中运行命令。如果虚拟环境还不存在,运行这个命令会创建虚拟环境。
$ poetry run which python
/tmp/myproj2/.venv/bin/python
$ poetry run flask --version
Python 3.13.5
Flask 3.1.3
Werkzeug 3.1.6
依赖管理
管理 Python 版本
较新版本的 Poetry 支持安装特定版本的 Python 了。先看看当前虚拟环境中的 Python 版本。
$ poetry env info
Virtualenv
Python: 3.13.5
Implementation: CPython
Path: /tmp/myproj2/.venv
Executable: /tmp/myproj2/.venv/bin/python
Valid: True
Base
Platform: linux
OS: posix
Python: 3.13.5
Path: /usr
Executable: /usr/bin/python3.13
和系统 Python 版本一致,是 3.13。Poetry 的 python 子命令有 install、list、remove 3 个子命令管理项目的 Python 版本。
$ poetry python list --all
Version Implementation Manager Path
3.14.3t CPython Poetry Available for download
3.14.3 CPython Poetry Available for download
...
上面的命令会列出所有可用的 Python 版本,Available for download 表示可以下载安装。如果不使用 --all 参数,只会列出本地已有的 Python 版本。
安装 Python 3.11 试试看:
$ poetry python install 3.11
Downloading and installing 3.11 (cpython) ... Done
Testing 3.11 (cpython) ... Done
$ poetry python list
Version Implementation Manager Path
3.13.5 CPython System /usr/bin/python3.13
3.13.5 CPython System /usr/bin/python3
3.13.5 CPython System /bin/python3.13
3.13.5 CPython System /bin/python3
3.11.14 CPython Poetry ~/.local/share/pypoetry/python/cpython@3.11.14/bin/python3.11
3.11.14 CPython Poetry ~/.local/share/pypoetry/python/cpython@3.11.14/bin/python3
3.11.14 CPython Poetry ~/.local/share/pypoetry/python/cpython@3.11.14/bin/python
安装过程就是到 GitHub 的 python-build-standalone 仓库下载对应版本的 Python 包安装。
可以看到 Poetry 把 Python 3.11 安装到 ~/.local/share/pypoetry/python/ 目录下了。
然后修改 pyproject.toml 文件,编辑 requires-python 一行,指定使用 Python 3.11 版本:
requires-python = ">=3.11,<3.12"
删除旧的 3.13 版本虚拟环境,然后切换为 3.11 并重建虚拟环境。
$ poetry env remove
Deleted virtualenv: /tmp/myproj2/.venv
$ poetry update
Creating virtualenv app in /tmp/myproj2/.venv
Writing lock file
$ poetry env info
Virtualenv
Python: 3.11.14
Implementation: CPython
Path: /tmp/myproj2/.venv
Executable: /tmp/myproj2/.venv/bin/python
Valid: True
Base
Platform: linux
OS: posix
Python: 3.11.14
Path: ~/.local/share/pypoetry/python/cpython@3.11.14
Executable: ~/.local/share/pypoetry/python/cpython@3.11.14/bin/python3.11
$ poetry run python --version
Python 3.11.14
先删除旧的虚拟环境,然后运行poetry update 会新建 3.11 版本的虚拟环境。新的 .venv 环境里的 Python 版本是 3.11,也显示了 3.11 环境基于 Poetry 安装在用户目录下的 Python。
管理依赖
从 PyPI 里查找包,可以看到把 aliyun 这个 PyPI 源中匹配 aiohttp 的包都列出来了。
$ poetry search aiohttp
Package Version Source Description
aiohttp 0.1 aliyun
aiohttp 0.2 aliyun
aiohttp 0.3 aliyun
aiohttp 0.4 aliyun
...
add 和 remove 子命令分别用来安装和卸载依赖,都会同步修改 pyproject.toml 文件。
$ poetry add "aiohttp>2,<3" httpx
$ poetry show aiohttp
name : aiohttp
version : 2.3.10
description : Async http client/server framework (asyncio)
...
使用 > 或者 < 限制版本时要使用引号,因为它们在 shell 中有重定向的含义。如果指定的版本和之前不一样,Poetry 会重新安装成新指定的版本,并更新 pyproject.toml 文件。
上面指定了 aiohttp 的版本范围,实际安装的是符合条件的最高版本。pyproject.toml 文件中也做了相应的变更。
$ grep -E ^dependencies -A 4 pyproject.toml
dependencies = [
"flask (>=3.1.3,<4.0.0)",
"aiohttp (>2,<3)",
"httpx (>=0.28.1,<0.29.0)"
]
上传项目到 Git 时,建议上传 pyproject.toml 和 poetry.lock 两个文件,不要上传 .venv。
注意:如果开发的是库(library),而不是应用(application),则往往不需要上传
poetry.lock到代码版本管理仓库,因为库需要保持多个依赖版本的兼容性,如果写死版本,很可能会产生依赖冲突。
当把这个项目迁移到新环境或者目录时,可以使用 poetry sync 解析并安装所有依赖,这个命令提供了 --dry-run 选项,可以只输出操作,不实际运行。
$ poetry sync
poetry add和pip install安装依赖时都会分析包的依赖,并解决依赖。在执行poetry remove时,Poetry 会删除依赖的库,而pip uninstall则不会,pip 只会删除指定库。
依赖组
有些依赖仅仅在开发阶段使用,比如 lint 工具;有些是在软件测试中用到的,比如 pytest。生产环境运行时应该保持最精简的状态和最小体积,不需要安装这些开发和测试阶段的依赖。为了区分依赖,可以新增 dev 和 test 两个可选(optional)依赖组,依赖组的名字可以随意指定,但建议使用有意义的词汇。
$ poetry add --group dev ruff autopep8
$ poetry add --group test pytest
$ grep -A7 "dependency-groups" pyproject.toml
[dependency-groups]
dev = [
"ruff (>=0.15.4,<0.16.0)",
"autopep8 (>=2.3.2,<3.0.0)"
]
test = [
"pytest (>=9.0.2,<10.0.0)"
]
从结果可以看到,pyproject.toml 中多了 [dependency-groups] 子表,内容是指定安装的依赖。在 [project] 中 dependencies 里定义的依赖是 main 组。
默认情况下,install 命令会安装所有依赖组。使用 --with 安装可选组,使用 --without 排除可选组,使用 --only 只安装某些组。
$ poetry install --only=dev,main
$ poetry install --without=test
在目前的 pyproject.toml 配置下(只有 main、dev、test 这三个组),上面两个命令的效果等价。
注意:使用 --only 时,Poetry 会自动忽略 --with 和 --without 选项。
使用 show 子命令可以查看依赖的安装情况,默认会把间接依赖也列出来,使用 --tree 以树形方式展示依赖关系,使用 --top-level 选项只列出 pyproject.toml 文件中指定的顶级依赖。
poetry.lock 文件
上面说过,poetry.lock 文件详细记录了当前项目所有依赖包的精确版本信息,包括直接依赖和间接依赖,相当于当前项目依赖情况的一个快照。不要手动修改 poetry.lock 文件。
如果手动修改了 pyproject.toml 文件的依赖项,可能会导致 poetry.lock 中的依赖不一致。运行 poetry install 时会报错:
$ poetry install
Installing dependencies from lock file
pyproject.toml changed significantly since poetry.lock was last generated. Run `poetry lock` to fix the lock file.
$ poetry lock
Resolving dependencies... (3.0s)
Writing lock file
poetry install 时的输出提示 pyproject.toml 和 poetry.lock 文件内容不同步,需要运行 poetry lock 来更新 poetry.lock 文件。
注意:poetry lock 这个命令只会更新 poetry.lock 文件的依赖条目,并不会安装依赖。而且这个过程会遍历所有直接依赖的依赖项,以便解决潜在的依赖版本冲突问题。
依赖锁定主要解决如下两个问题:
+ 解析:找到满足所有依赖限制下的解决方案,避免出现间接依赖的版本冲突。
+ 锁定:通过 poetry.lock 文件为当前所有依赖项创建快照。
使用
poetry lock --regenerate会忽略已有的 poetry.lock 文件,重新创建一个 poetry.lock 文件覆盖旧的。
执行 poetry install 会安装 poetry.lock 文件中的所有依赖,且只安装环境中缺失的那部分。
如果环境中存在 poetry.lock 和 pyproject.toml 中未记录的依赖怎么办?比如,使用 pip 往虚拟环境中安装的依赖。使用 poetry sync 不止会依赖检查、安装和同步,也会清理环境中未被记录的依赖。
$ poetry run pip install requests
$ poetry sync
Installing dependencies from lock file
Package operations: 0 installs, 0 updates, 3 removals
- Removing charset-normalizer (3.4.4)
- Removing requests (2.32.5)
- Removing urllib3 (2.6.3)
Installing the current project: app (0.1.0)
使用 sync 子命令,Poetry 移除了虚拟环境中没有被 poetry.lock 文件记录的依赖,使得虚拟环境和 poetry.lock 的依赖保持一致。
poetry install 和 poetry sync 命令的功能十分相似,都可以安装指定的依赖项,但有两点不一样:
poetry install不会清理环境中 Poetry 未记录的依赖,而poetry sync会清理。sync比install更严格。- 如果是 package 项目,
poetry install会把当前的项目作为依赖安装。
依赖升级
Poetry 的 show 子指令可以查看项目依赖的情况:
$ poetry show aiohttp
$ poetry show --latest
- 查看 aiohttp 包的版本和依赖关系
--latest查看依赖可用的最新版本
如果依赖有满足条件的新版本,可以使用 update 子命令进行更新。
$ poetry update aiohttp
$ poetry update
以上示例分别用于更新指定的依赖和更新所有依赖。update 也支持 --dry-run 选项,由于版本升级总是伴随冲突风险,建议升级前查看具体升级了哪些依赖。
如果要突破 pyproject.toml 文件中的版本限制进行升级,比如 aiohttp 2.3.10 升级到 aiohttp 3.3.0,或者直接升级到最新版本,需要通过如下的 add 指令操作,这个命令也会同步修改 pyproject.toml 中的依赖版本限制。
$ poetry add "aiohttp<3.10"
$ grep aiohttp pyproject.toml
"aiohttp (<3.10)",
$ poetry show aiohttp
name : aiohttp
version : 3.9.5
...
升级依赖版本和新增依赖一样,都是 add 子命令;通过新的版本约束触发重新解析并升级到符合条件的版本。
虚拟环境
如果 Poetry 的全局配置项 virtualenvs.in-project=false,Poetry 项目的虚拟环境会放在用户目录下的 .cache/pypoetry/virtualenvs,可以为一个项目支持多个不同的 Python 环境,以便切换多个版本进行测试。当 virtualenvs.in-project=true,项目的虚拟环境就是项目目录下的 .venv 目录,就无法再支持多版本了。
Poetry 在运行时,会通过检查 pyproject.toml 文件检测当前是否在 Poetry 环境中,是否已经有虚拟环境。如果 Poetry 项目中还没有虚拟环境,Poetry 会自动创建虚拟环境后执行命令。下面是临时把 virtualenvs.in-project 改为 false 之后,测试多个版本的 Python 环境。
$ poetry config virtualenvs.in-project false # 设置虚拟环境为用户目录下,而非项目目录下
$ sed -i 's/^requires-python.*$/requires-python = ">=3.11"/' pyproject.toml # 修改项目的python依赖为 >= 3.11
$ poetry env use 3.11 # 切换 Python 3.11
Creating virtualenv app-X2aZMY7n-py3.11 in ~/.cache/pypoetry/virtualenvs
Using virtualenv: ~/.cache/pypoetry/virtualenvs/app-X2aZMY7n-py3.11
$ poetry run python --version # 验证当前 Python 版本
Python 3.11.14
$ poetry env use 3.13 # 切换 Python 3.13
Creating virtualenv app-X2aZMY7n-py3.13 in ~/.cache/pypoetry/virtualenvs
Using virtualenv: ~/.cache/pypoetry/virtualenvs/app-X2aZMY7n-py3.13
$ poetry run python --version # 验证当前 Python 版本
Python 3.13.5
$ poetry env info --path # 虚拟环境的路径
~/.cache/pypoetry/virtualenvs/app-X2aZMY7n-py3.13
$ poetry env list
app-X2aZMY7n-py3.11
app-X2aZMY7n-py3.13 (Activated) # 当前启用的 Python 版本
以上命令使用 env use 创建了 Python 3.11 和 Python 3.13 的虚拟环境,后一个虚拟环境 Python 3.13 目前处于 Activated 状态。不同版本的虚拟环境有不同的虚拟环境目录,切换不同 Python 版本很方便验证项目在各个版本上的可用性。指定的虚拟环境版本必须符合 pyproject.toml 中对 Python 依赖的规定。
虚拟环境的路径中有一串看似无意义的随机字符串(X2aZMY7n),实际是对项目路径做了 hash 之后的 base64 编码,如果项目路径发生变化,虚拟环境也会重新生成。
poetry env remove --all 删除所有虚拟环境,也可以将 --all 替换为具体的版本,删除特定版本。
$ poetry env remove --all
进入虚拟环境
在写代码或者某些场景中,可能需要进入虚拟环境操作,方法如下:
$ poetry env activate # 查看激活虚拟环境的方法
source ~/.cache/pypoetry/virtualenvs/app-X2aZMY7n-py3.13/bin/activate
$ source ~/.cache/pypoetry/virtualenvs/app-X2aZMY7n-py3.13/bin/activate # 激活虚拟环境
(app-py3.13) $ which python # 检查虚拟环境
~/.cache/pypoetry/virtualenvs/app-X2aZMY7n-py3.13/bin/python
(app-py3.13) $ deactivate # 退出虚拟环境
和正常激活虚拟环境的区别不大,主要是找到当前的虚拟环境路径。
运行测试项目
开发流程中的 Poetry
在使用 Poetry 管理的项目中,开发流程中常涉及的管理动作如下:
+ poetry new 创建项目。
+ poetry add/remove 管理项目依赖
+ 开发项目
+ poetry run 运行和测试项目
+ poetry sync 更新本地的依赖情况
一般来说,pyproject.toml 文件中的某些信息也是需要偶尔手动维护的。
实例
新建项目
$ poetry new simple-web --flat
$ cd simple-web
$ poetry add flask
修改 app/__init__.py 文件为如下内容,是一个最简单的 Flask Web 应用:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "Hello, World"
运行方法:
$ FLASK_APP=app:app poetry run flask run
小结
Poetry 把依赖管理、虚拟环境与构建发布整合到统一流程中,使用门槛低、可重复性强。对应用型项目,建议优先明确依赖组与 Python 版本约束;对库项目,则更应关注依赖范围与锁定策略的边界。值得关注的是,Poetry 之外还有势头正猛的后起之秀 uv,将在下一篇博客中介绍。
Last updates at Mar 01,2026
Views (7)
total 0 comments