Poetry: Python packaging and dependency management made easy¶
背景¶
Poetry 是一个Python 中的好用的包管理工具。在 Python 中,打包系统和依赖管理非常复杂:一个项目经常要同时创建多个文件,例如:
- setup.py
- requirements.txt
- setup.cfg
- MANIFEST.in
- Pipfile
基于此, poetry 将所有的配置都放置在一个 .toml 文件中,包括:依赖管理、构建、打包、发布等,可谓是简单方便。
Poetry用一个简单的基于 pyproject.toml 的项目格式取代了 setup.py 、 requirements.txt 、 setup.cfg 、 MANIFEST.in 和 Pipfile 。
原理¶
-
pyproject.toml:这是一个配置文件,它用于定义项目的元数据和依赖关系。在pyproject.toml文件中,您可以指定项目的名称、版本、作者等信息,并列出项目所需的依赖项及其版本范围。该文件还可以包含构建和发布项目的相关配置。 -
poetry.lock:这是一个生成的锁定文件,它记录了确切的依赖关系版本。当运行poetry install命令时,Poetry会解析pyproject.toml文件并生成poetry.lock文件。锁定文件中列出了每个依赖项及其确切的版本号。这确保了在不同的开发环境或部署环境中都使用相同的依赖版本,从而增加项目的可重现性和稳定性。 实际上就相当于pip 的requirements.txt,详细记载了所有安装的包与版本。
区别:pyproject.toml是用于定义项目的元数据和依赖关系的配置文件,而poetry.lock是根据pyproject.toml文件生成的锁定文件,记录了确切的依赖版本。
pyproject.toml提供了依赖范围(例如,指定一个范围的最小和最大版本),而poetry.lock提供了精确的版本号。
作为应用程序开发人员:您应该将该poetry.lock文件提交到您的项目存储库,以便所有从事该项目的人员都被锁定到相同版本的依赖项。
作为库开发人员:库开发人员需要考虑更多。您的用户是应用程序开发人员,您的库将在您无法控制的 Python 环境中运行。您应该省略poetry.lock文件。
安装¶
Poetry的安装需要 Python 环境来运行,因为默认会采用Python虚拟环境的方式安装Poetry。
installing-with-the-official-installer
默认情况下,Poetry 安装到特定于平台和用户的目录中:
~/Library/Application Support/pypoetry在 MacOS 上。~/.local/share/pypoetry在 Linux/Unix 上。%APPDATA%\pypoetry在 Windows 上。
更新Poetry¶
配置¶
可以通过config命令进行配置,也可以在配置文件中配置
- macOS:
~/Library/Application\ Support/pypoetry
| Bash | |
|---|---|
使用¶
创建一个新的Python项目¶
| Bash | |
|---|---|
把已有的项目交给Poetry来管理¶
| Bash | |
|---|---|
- 只会生成
pyproject.toml文件,不会生成lock文件。 - 在生成
pyproject.toml文件中可以看到其中有一行「readme = "README.md"」,此时项目目录中必须要有README.md,否则会找不到并在未来使用中可能报错。我都直接刪除该行,比较省事。
接手别人的Poetry项目,并一键安装所需的依赖¶
-
如
pyproject.toml文件不存在,则会报错。 -
如不存在virtualenv,则会自动创建virtualenv,虚拟环境目录为
{project-dir}/.venv前提是配置了poetry config virtualenvs.create true -
从当前项目中读取
pyproject.toml文件,解析依赖项并安装这些模块。如果当前目录中有poetry.lock文件,它将使用lock那里的确切版本而不是解析它们。这可以确保使用该库的每个人都将获得相同版本的依赖项。
如果没有poetry.lock文件,Poetry 会在依赖解析后创建一个lock文件。
-
poetry install预设安装pyproject.toml中的所有环境的所有包!所以如果你只想安装main环境的即 [tool.poetry.dependencies] 区块的包,请务必使用--only main。 -
--with:代表「和」的关系,也就是说会安装main的包也要安装指定环境的包。
生成lock文件¶
| Bash | |
|---|---|
-
当你自行修改了
pyproject.toml内容,比如变更特定包的版本(这是有可能的,尤其在手动处理版本冲突的时候),此时poetry.lock的内容与pyproject.toml出现了「脱钩」,必须让它依照新的pyproject.toml内容更新、同步,使用指令poetry lock。如此一来,才能确保手动修改的内容,也更新到poetry.lock中,毕竟虚拟环境如果要重新建立,是基于poetry.lock的内容来安装包,而非pyproject.toml。 -
poetry.lock相当于Poetry 的requirements.txt。 -
poetry lock指令,仅会更新poetry.lock,「不会」同时安装包至虚拟环境。 因此,在执行完poetry lock指令后,你必须再使用poetry install来安装套件。 否则就会出现poetry.lock和虚拟环境不一致的状况。
搜索远程模块¶
安装模块¶
-
如不存在virtualenv,则会自动创建virtualenv,前提是配置了
poetry config virtualenvs.create true -
会自动配置
pyproject.toml -
如
pyproject.toml文件不存在则会报错。 -
在以有pyproject.toml 文件的情况下,如不存在
poetry.lock,则会自动创建poetry.lock -
如果您尝试添加已存在的包,您将收到错误消息。
-
有些包,比如
pytest、flake8等等,只会在开发环境中使用,产品的部署环境并不需要。Poetry 允许你区分这两者,将上述的包安装至group.dev.dependencies区块,方便让你轻松建立一份「不包含」group.dev.dependencies开发包的安装清单。需使用--group参数。明确区分开发环境专用的套件,我认为非常必要。
常见的group.dev.dependencies区块项目,例示如下:
| TOML | |
|---|---|
-
当你使用
poetry add指令时,Poetry 会自动依序帮你做完这三件事: -
更新
pyproject.toml。 - 依照
pyproject.toml的内容,更新poetry.lock。(相当于poetry lock) -
依照
poetry.lock的内容,更新虚拟环境。(相当于poetry install) -
当你不是使用
poetry add指令,而是直接修改pyproject.toml时,此时上述的第 2、3 步都不会自动执行。但通常你手动修改 toml 档最终都是为了变更虚拟环境,所以更新完pyproject.toml后,我们还要再使用poetry lock然后再执行poetry install指令才行! -
poetry add只会在pyproject.toml中写入「主包」即top-level的包名,但cat requirements.txt | xargs poetry add这样的import方式相当于把requirements.txt中的所有包,都当作主包来add了! 因为Poetry对套件的版本冲突比较敏感,所以仍有机会出现错误,只能照着错误讯息手动修正。毕竟在requirements.txt中无从区分主包与依赖包,都是「一视同仁」地列出。但如此做法也让项目的包失去主从之分,日后要移除主包时,需要花额外的心力去区分主从。
更新模块¶
| Bash | |
|---|---|
- 注意:这不会更新
pyproject.toml文件中指定的版本约束之外的依赖项的版本。所以如果要更新需要先修改pyproject.toml文件中指定的版本约束,而后再进行更新。 即,关于安装包版本的升级限制规则,取决于你在pyproject.toml中的设定。
查看项目安装的模块¶
| Bash | |
|---|---|
poetry show类似pip list,这里的清单内容并不是来自于虚拟环境,这点和pip不同,而是来自于poetry.lock的内容。- 虚拟环境和
poetry.lock也有不一致的时候,比如你使用了pip install指令安装包,就不会记载在poetry.lock中,那poetry show自然也不会显示。 - 如果
poetry.lock文件不存在,则会报错并提示运行poetry lock命令进行创建。 - 红色代表当前环境未安装此包,但是其声明于
pyproject.toml文件中。 -t会导致颜色不准,即未安装显示已安装。(发现的Bug,已提issue)
卸载依赖模块¶
| Bash | |
|---|---|
- 会自动配置pyproject.toml
- 会在删除目标包的同时把其依赖包也删除,这就是poetry的依赖解析(相依性管理)能力,这是pip所不具备的,因为pip 的
pip uninstall只会移除你所指定的包,而不会连同依赖包一起移除。 Flask和Black这两个top-level包都共同依赖click这个包,如果我们使用poetry remove flask则不会移除依赖包click,因为有依赖解析,Poetry 知道Black还需要click!所以不能移除。- 一个包直到环境中的其余包都不再依赖它,Poetry 才会安心让它被移除。
导出¶
- hash 有其价值,并建议保留。默认情况下,pip 不执行任何检查来防止远程篡改,并涉及运行发行版中的任意代码。哈希好处
poetry export预设只会输出pyproject.toml中的[tool.poetry.dependencies]区块的包!因为大部分时候我们并不需要输出开发用包。
构建&发布¶
| Bash | |
|---|---|
虚拟环境相关¶
| Bash | |
|---|---|
检查¶
源配置¶
- 包源的配置是针对项目本地的,必须在项目的
pyproject.toml文件中配置,即只针对当前项目有效。 - 默认情况下,Poetry被配置为使用 Python 生态系统的规范包索引 PyPI。
- 优先级:
default>primary>implicit PyPI>supplemental>explicit default:primary:如果priority未定义,则源被视为primary。implicit:隐式源;pypi默认就是这个。supplemental:补充源;可以有多个补充包源。仅当没有其他(更高优先级)源产生兼容的包分发时,才会搜索配置为补充的包源。explicit:显式源;即仅对于明确指定其来源的包才会使用显式源。- 在未来版本的Poetry中,如果至少有一个自定义源配置的优先级高于
explicit,PyPI将自动禁用。
| Bash | |
|---|---|
Poetry 常见使用情境¶
新增项目并使用Poetry¶
这是最理想的状态,没有过去的「包袱」,可谓是最能轻松采用Poetry 的情境。
我的使用顺序是:
poetry init:初始化,只会在当前目录中新建一个pyproject.toml文件。poetry env use python(可省):建立项目的虚拟环境。poetry shell:使用此指令进入虚拟环境。 如果使用本指令时虚拟环境尚未建立或已移除,则会直接自动帮你建立虚拟环境并进入。poetry add <PackageName>:安装指定包并写入虚拟环境。必要时使用--group dev参数,使其安装至dev 区块。poetry remove <PackageName>:移除指定包,若是移除dev区块的套件,需要加上--group dev参数。
现有项目改用Poetry¶
极为常见的需求
poetry init:初始化,只会在当前目录中新建一个pyproject.toml文件。poetry shell:使用此指令进入虚拟环境。 如果使用本指令时虚拟环境尚未建立或已移除,则会直接自动帮你建立虚拟环境并进入。- 安装
cat requirements.txt | xargs poetry add这样的import方式相当于把requirements.txt中的所有包,都当作主包来add了! 因为Poetry对套件的版本冲突比较敏感,所以仍有机会出现错误,只能照着错误讯息手动修正。毕竟在requirements.txt中无从区分主包与依赖包,都是「一视同仁」地列出。但如此做法也让项目的包失去主从之分,日后要移除主包时,需要花额外的心力去区分主从。
在别台主机上重现项目的Poetry 虚拟环境¶
第一步当然是git clone专案,此时专案中已经有Poetry 所需的必要资讯了——也就是pyproject.toml和poetry.lock。
你还缺少的仅仅是虚拟环境。
-
poetry shell:使用此指令进入虚拟环境。 如果使用本指令时虚拟环境尚未建立或已移除,则会直接自动帮你建立虚拟环境并进入。 -
poetry install:因为是旧专案,不需要init,会直接依poetry.lock记载的套件版本安装到虚拟环境中!类似npm install。
我想要移除并重建虚拟环境¶
- 直接删除
.venv文件夹即可。 - 然后再
poetry env use python或poetry shell建一个新的就好。
为什么我不在Docker 环境中使用Poetry?¶
因为启动容器后需要先安装Poetry 到全域,或打包一个带有Poetry 的image,两者都会增加新的耦合与依赖,我觉得并不妥当。
解决方案:
- 使用multi-stage builds的Dockerfile,可以在第一阶段安装Poetry,第二阶段再把Poetry 舍弃,这样就不会有多余的耦合与依赖了。
- 干脆使用Poetry 输出
requirements.txt,Docker 部署环境就继续使用这个旧方案即可。
Poetry 命令速查表¶
| Poetry Command | 解释 |
|---|---|
$ poetry --version |
显示您的 Poetry 安装版本。 |
$ poetry new |
创建一个新的Poetry项目。 |
$ poetry init |
将 Poetry 添加到现有项目中。 |
$ poetry run |
使用 Poetry 执行给定的命令。 |
$ poetry add |
添加一个包pyproject.toml并安装它。 |
$ poetry update |
更新项目的依赖项。 |
$ poetry install |
安装依赖项。 |
$ poetry show |
列出已安装的软件包。 |
$ poetry lock |
将最新版本的依赖项固定到poetry.lock. |
$ poetry lock --no-update |
刷新poetry.lock文件而不更新任何依赖版本。 |
$ poetry check |
验证pyproject.toml。 |
$ poetry config --list |
显示 Poetry 配置。 |
$ poetry env list |
列出项目的虚拟环境。 |
$ poetry export |
导出poetry.lock为其他格式。 |
pyenv & poetry联合使用¶
- 因为 Poetry 自带了虚拟环境管理功能,容易和 pyenv-virtualenv 叠床架屋,徒增管理上的混淆,所以我现在一律只使用 Poetry的虚拟环境来管理 Python虚拟环境。
- 即使在不同项目需要多版本 Python 情況下,pyenv-virtualenv 也不是必须。只要善用
pyenv local和poetry env use两大指令即可。
| Bash | |
|---|---|
4. 参考文档¶
https://blog.kyomind.tw/python-poetry/
https://blog.kyomind.tw/poetry-pyenv-practical-tips/
创建日期: July 18, 2023