Published on

PDM 2.0 有什么新特性?

Reading Time

11 min read

pdm-2.0

The English Version

PDM 在最近发布了 2.0.0 版本,新特性已基本完成。本文将介绍这次更新的内容。详细改动日志在这里可以看到。

虚拟环境成为项目的默认配置

PDM 在建立之初,是标榜自己是一个支持 PEP 582 包结构的包管理器。但无奈,经过两年的观望,PEP 582 仍旧停留在 Draft 状态,并且迟迟没有进展。 它虽然在一开始令人眼前一亮并吸引了大批初始用户,但这也成为PDM 被主流接纳的一个阻碍,限制了它的推广。同时,虚拟环境在各种 IDE 和工具中有更好的支持。 现在我希望 PDM 并不仅是一个个人兴趣的项目,并且是一个支持 Python 打包最新规范的正经的包管理器,所以在 2.0 中,我们将虚拟环境成为项目的默认配置。

  1. pdm init 中,如果你没有选择一个已有虚拟环境中的解释器,PDM 会询问是否需要创建一个新的虚拟环境。如果创建,则会把这个虚拟环境作为项目环境, 否则,还是会启用 PEP 582 包结构。
  2. 当你克隆一个已有的项目,在项目中第一次执行 pdm install 时,PDM 会检查项目中是否存在一个 __pypackages__ 文件夹1,如果存在,会使用 PEP 582 包结构, 否则会自动为你创建一个虚拟环境并在其中安装依赖。

我们尽可能保证旧的项目不会变化,而只是新项目的默认方式变了。在文档中,PEP 582 也从首页最显眼的位置移动到了子页面中。所以 PDM 依然支持 PEP 582,只是不是默认的方式。

PDM 搭配其他后端

PDM 虽然有一个自己的后端2 pdm-pep517 但它其实没有和任何后端绑定,你依然可以使用比如 flit-core, hatchling, setuptools 作为后端,只要它支持读取 PEP 621 的元数据。甚至,你可以混用其他的包管理工具,例如,你完全可以用 flit 来打包你的项目,而只把 PDM 作为依赖管理的工具来使用,因为前者不具备依赖管理的功能。

这就是拥抱标准所带来的巨大好处——工具只要遵循同一个规范,它们之间就可以共同存在,各自完成自己的工作。

不再允许在项目依赖中包含 Editable 的包

原先,在 pyproject.toml 中的 [project]:dependencies 中你可以包含类似 -e ./mypackage, -e git+https://github.com/psf/requests.git@main 这样的 以 Editable 方式安装的包。但这是不符合 PEP 631 规范的,所以在 2.0 中,我们不再支持这种依赖,已有的 editable 包会弹出警告。

但别担心,你还是可以在 [tool.pdm.dev-dependencies] 中包含 Editable 的包,因为实际上它们只在开发中有用。

PDM 全局配置路径遵循 XDG 目录规范

原先 PDM 的全局配置是存在 ~/.pdm 下面的,但在 2.0 中,它们将被放置在 $CONFIG_HOME 下面。具体的值为:

  • Linux: $XDG_CONFIG_HOME/pdm (一般为 ~/.config/pdm)
  • MacOS: ~/Library/Preferences/pdm
  • Windows: %USERPROFILE%\AppData\Local\pdm

你需要做一次性迁移(Linux 为例):

$ mv ~/.pdm ~/.config/pdm

感谢 @noirbizarre 的贡献。

增加 pdm publish 命令

是的,这个功能是很多用户都希望拥有的,我们终于在 PDM 2.0 中加上了!直接执行 pdm publish,PDM 会自动打包项目,然后上传到 PyPI。当然,在这之前, 你需要配置好上传所需要的用户密钥。

$ pdm config repository.pypi.username <username>
$ pdm config repository.pypi.password <password>

UI 的 rich 化

PDM 2.0 把 UI 的渲染从原来的 click + halo 改成了 rich,后者提供了一站式的体验和灵活的配置,构建出了强大而美观的 UI。大部分 UI 都保持了原样,但 rich 的实现更加简单,也更少的 bug。

pdm add pdm list

感谢 @daylinmorgan 的贡献。

不再依赖 pip 内部的 API

PDM 1.x 中寻找包和下载包的部分用到了部分 pip 的 API,但 pip 从来不是作为一个库使用的,而且它遵循的是 CalVer 版本发布,所以即使在小版本的升级中 也会破坏 API 的兼容性,导致 PDM 坏掉。从前 PDM 只能在依赖中限定 pip 的版本范围,但问题是 pip 作为一个基础工具,在不同的 Linux 发行版中可能有各种 patch 导致不能兼容。 所以我们彻底摒弃了使用 pip 的内部 API,转而自己造了一个轮子 unearth 来使用。这将增加稳定性,也方便了下游的打包者。

全面强化的用户脚本系统

在 PDM 之前的版本中我们已经加入了用户脚本系统([tool.pdm.scripts],类似 package.json 中的 scripts),在 2.0 版本中,我们继续增加了许多功能, 让这个系统变得更加强大和灵活。

感谢 @noirbizarre 的贡献。

composite 复合脚本

你可以使用复合脚本来组合多个脚本,它们之间将顺序执行,其中有任一脚本失败,整个复合脚本就失败。

[tool.pdm.scripts]
lint = "flake8"
test = "pytest"
all = {composite = ["lint", "test"]}

运行 pdm run all 就会执行 linttest

用户脚本作为根命令执行

如果你有一个脚本 start,那么 pdm startpdm run start 同样会执行这个脚本,只要脚本名称没有和其他的命令冲突。

增加更多的钩子

钩子(hook)是在 PDM 执行特定事件前后会触发的回调动作,这可以让插件的开发更加容易。在 PDM 2.0 中我们新增以下钩子:

  • pre_publish/post_publish:在上传发布包之前和之后触发
  • pre_run/post_run:在运行 pdm run 之前和之后触发
  • pre_script/post_script:在运行单个脚本之前和之后触发
  • post_use:在使用 pdm use 切换 Python 解释器之后触发

具体使用方法请查阅文档

感谢 @noirbizarre 的贡献。

增加 --skip 选项跳过某些钩子或脚本

有了如此多的钩子,很多时候你并不希望它们全都触发,可以使用 --skip 选项来跳过某些钩子或脚本。

# 跳过 pre_install
pdm install --skip pre_install
# 跳过 pre_publish 和 post_publish
pdm publish --skip pre_publish,post_publish
# 跳过所有 post_* 以及 pre_start 钩子和脚本
pdm run --skip:post --skip pre_start start

感谢 @noirbizarre 的贡献。

致谢

在 PDM 2.0 开发中收到了很多帮助、反馈和贡献,特别是 @noirbizarre(他是 flask-restplus 的作者)贡献了大部分用户脚本的功能。 在这里我想贴上他在一个讨论中对 PDM 的评价,引用就不翻译了:

I think the PEP-582 support even if opt-in is already an argument.

Given I started using PDM recently after a painful year (maybe 2) looking for a decent solution (Python project management and packaging have been hell recently):

  • it just work, no tricks need, no special version (seems logical but just try any other and you will understand)
  • scripts support (game changer to me, Poetry only have it through poet and Pipenv ones are limited (no env, no shell chaining, no composition...)
  • supports PEP-621: Poetry and Pipenv don't, setuptools has a just recently experimental support
  • lock file support + pnpm-like cache
  • editables support (only project so far having both PEP-621 and editables support)
  • single tool batteries included (setuptools and flit don't cover the full workflow, poetry is going under heavy refactoring leading to 2 non-stable codebases, and Pipenv does not support packaging and extensions are very limited, mostly IDE integration)
  • powerful extensions support + dynamic ecosystem: (I can attest it, I contributed a lot lately, everything has been reviewed and accepted, very quickly and the tone was good/kind). I can add transparent and realistic roadmap
  • great doc !
  • extensive test suite
  • have I said it just work 🎉

And I'm sure some points are missing from the list.

To make it clear: PDM is as of today the only Python project management and packaging tool covering the full lifecycle, supporting all recently published PEP on packaging, supporting lock and being able to handle both libs and apps. (And this this why I'm currently in the process of migrating all my projects on PDM)

安装试用新版 PDM

欢迎使用和测试新版 PDM。

如果是用 install-pdm.py 安装:

curl -sSL https://raw.githubusercontent.com/pdm-project/pdm/main/install-pdm.py | python3 - --prerelease
# Or Windows powershell:
(Invoke-WebRequest -Uri https://raw.githubusercontent.com/pdm-project/pdm/main/install-pdm.py -UseBasicParsing).Content | python - --prerelease

如果是用 pipx 安装:

pipx install --pip-args="--pre" pdm
# 或者更新已有
pipx upgrade --pip-args="--pre" pdm

Footnotes

  1. 你只需要上传一个空的 __pypackages__ 到 git 上,而把安装的包 ignore 掉

  2. 在 Python 打包中后端是指读取元数据进行构建、打包的工具(如 setuptools),而前端是指提供用户界面以修改元数据的工具(如 pip)

Share: