使用Poetry打包Python项目并发布

https://www.do1e.cn/posts/code/poetry

起因

目前维护这两个 Python 轮子：

之前一直使用的是setup.py进行打包，不过 11 月份更新NJUlogin时发现这样的一个提示：

Copy
********************************************************************************
With Python 2.7 end-of-life, support for building universal wheels
(i.e., wheels that support both Python 2 and Python 3)
is being obviated.
Please discontinue using this option, or if you still need it,
file an issue with pypa/setuptools describing your use case.

By 2025-Aug-30, you need to update your project and remove deprecated calls
or your builds will no longer be supported.
********************************************************************************

因此决定使用新的pyproject.toml的方式进行打包。当时临时学了Poetry并给NJUlogin做了适配，今天更新mijia-api想顺便也做一个适配，结果发现忘了怎么用，还是写个博客记录一下吧。看来之后也多多用Poetry来管理依赖了。

原本的打包脚本
```powershell remove-item dist -force -recurse python setup.py sdist build python setup.py bdist_wheel --universal twine upload dist/* ```

Poetry

Poetry官网上对自身的定位是：Python packaging and dependency management made easy。用起来有点像npm（如果你对 nodejs 有一定了解的话），也是通过命令行修改管理依赖的文件。

不过 poetry 有点太长了，从现在开始你就叫pop吧。

Copy
alias pop='poetry'

Copy
Set-Alias pop poetry

安装

直接查看文档吧： https://python-poetry.org/docs/#installation
我这里用的是 pipx 进行安装

初始化已有项目

Copy
pop init

得到下述pyproject.toml文件

Copy
[tool.poetry]
name = "mijiaapi"
version = "0.0.0"
description = "A Python API for Xiaomi Mijia"
authors = ["Do1e <dpj.email@qq.com>"]
license = "GPLv3"
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.12"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

不过因为我的包里面包含大写字母，需要把[tool.poetry]下面的name改了的同时，再额外手动加一个packages项，另外再把setup.py中的一些其他配置加上来：

Copy
2c2
< name = "mijiaapi"
---
> name = "mijiaAPI"
7a8,17
> homepage = "https://github.com/Do1e/mijia-api"
> repository = "https://github.com/Do1e/mijia-api"
> packages = [
> {include = "mijiaAPI"}
> ]
> classifiers = [
> "Programming Language :: Python :: 3",
> "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
> "Operating System :: OS Independent",
> ]

新建 Peotry 虚拟环境并安装依赖

默认会在~/.cache/pypoetry里创建虚拟环境，可以使用pop config进行修改，详见文档这里，我个人更习惯放在项目目录中：

Copy
# 配置虚拟环境放在项目目录中
pop config virtualenvs.in-project true
# 新建并使用虚拟环境
pop env use python
# 如果是在conda环境中使用就不需要了，会直接使用已有conda环境
source .venv/bin/activate

之后便可以从requirements.txt中将依赖一个一个安装上了，下面这个命令会将依赖写入pyproject.toml文件中并生成poetry.lock：

Copy
pop add requests qrcode

Peotry 的一大优势就是能够以树状形式展示依赖：

Copy
pop show --tree
# qrcode 8.0 QR Code image generator
# └── colorama *
# requests 2.32.3 Python HTTP for Humans.
# ├── certifi >=2017.4.17
# ├── charset-normalizer >=2,<4
# ├── idna >=2.5,<4
# └── urllib3 >=1.21.1,<3

至此，可以将原来的setup.py删除了。

打包并发布

版本可以使用poetry-dynamic-versioning动态指定，不用每次修改pyproject.toml指定版本号。

Copy
git tag v1.3.0
pop self add "poetry-dynamic-versioning[plugin]"
pop dynamic-versioning enable

此时就可以简单地使用一行命令构建了：

Copy
pop build

不像 twine 使用~/.pypirc保存 pypi 的 token，Poetry 需要额外进行配置：

Copy
pop config pypi-token.pypi pypi-xxxxxx

然后就可以发布了！

Copy
pop publish