PDM 是一款国人开发的类似 pip、conda 的 python 包管理器,允许将一个 python 项目所需的包都放在当前目录下,支持创建多种虚拟环境,也允许自定义命令。其开发受到 Node. Js 包管理器 pnpm 的影响
PDM 可基于同一个 python 解释器构建多个虚拟环境,因此比 conda 更轻量,比 pip 更灵活。本文我们了解 pdm 的基本用法

初始化

安装 pdm 推荐使用 pipx,这是一个 python 命令行工具安装器。Pdm 会被安装在 /home/username/.local/bin 目录下

sudo apt install pipx
pipx install pdm
pipx ensurepath

使用 pdm init 命令初始化当前 python 项目,会列出当前能找到的所有 python 解释器。如果你同时使用了 conda,无法检测到其他 conda 环境下的 python。选择 python 版本后,其他选项保持默认即可
初始化的目录结构形如

├── .gitignore
├── .pdm-python
├── .venv
│   ├── .gitignore
│   ├── bin
│   ├── lib
│   └── pyvenv.cfg
├── README.md
├── __pycache__
├── pyproject.toml
├── src
│   └── tplot
└── tests
    ├── __init__.py
    └── __pycache__

pyproject.toml 包含项目的构建元数据、项目的依赖环境,也可在此定义快捷命令,类似 JavaScript 的 package.json
pdm.lock 存储了详细的包版本,类似 JavaScript 的 package-lock.json, yarn.lock, package-lock.json
.venv 存储了虚拟环境,类似 JavaScript 的 node_modules
.pdm-python 存储当前项目使用的 python 路径
项目的所有代码写在 src/projname 下,测试文件写在 tests

依赖管理

可为项目添加各种依赖项,add 命令会在修改 pyproject.toml 的同时安装好需要的包

# 添加依赖项
pdm add pandas seaborn
# 加入本地包,注意路径必须以./开头
pdm add ./sub-package
# 添加仅开发时的依赖项
pdm add -dG test pytest
# 删除依赖项
pdm remove requests
# 列出安装的所有包
pdm list
# 列出依赖路径
pdm list pandas --tree
# 根据pyproject.toml安装依赖项
pdm install

以上是从 0 开始一个新项目的做法。但 pdm 如何接管老项目?或者如何在 git clone 之后,创建合适的运行环境?
其实,只要项目中存在 requirements.txt,调用 git init 之后,就可以选择将 requirements.txt 的依赖默认添加到 pyproject.toml
如果是 git clone 来的项目,再执行 pdm install 即可

激活默认环境

以上,pdm 默认创建名为 in-project 的虚拟环境,这也是当前项目的默认环境。可 pdm venv list 列出

(base) william@winLab:~/proj/tplot$ pdm venv list
Virtualenvs created with this project:
*  in-project: /home/william/proj/tplot/.venv

如果你使用 VS code,在 terminal 中 cd 到项目目录,使用 code . 打开项目,即可自动激活 in-project 虚拟环境。否则需要使用 pdm 命令,读取项目变量激活虚拟环境

(base) william@winLab:~/proj/xplot$ eval $(pdm venv activate in-project)
(xplot-3.10) (base) william@winLab:~/proj/xplot$ 

或者,你也可以用 shell 命令手动激活

(base) william@winLab:~/proj/xplot$ source .venv/bin/activate

快捷命令

pyproject.toml 中,可在 [tool.pdm.scripts] 部分自定义快捷命令

[tool.pdm.scripts]
start = "flask run -p 54321"

然后,直接执行以下命令,即可启动 flask

pdm run start

如果需要指定运行目录、环境变量,可分别指定 `start.cmd, start.working_dir, start.env
所有快捷命令共享的全局环境变量,可指定

[tool.pdm.scripts]
_.env_file = ".env"

预定义安装

你可以在项目目录预先放置 requirements.txt 文件,pdm init 会询问是否安装指定的包

Project is initialized successfully
Found following files from other formats that you may import:
0. /home/william/pdm_env/tt/requirements.txt (requirements)
1. don't do anything, I will import later.

切换 python 版本

可以通过 pdm 安装多个 python 版本,会保存在 /home/username/.local/share/pdm/python 目录。这些 python 版本对所有项目都可见

pdm python install 3.8
# 列出通过pdm安装的python
pdm python list

然后,基于 3.8 版本创建另一个虚拟环境,会保存在 /home/username/.local/share/pdm/venvs 目录。该虚拟环境仅对当前项目可见

pdm venv create --name test3.8 3.8

此时,如果激活虚拟环境 test3.8,pdm 记录的项目信息,仍然是默认的 in-project 环境

(xplot-3.10) (base) william@winLab:~/proj/xplot$ eval $(pdm venv activate test3.8)
(xplot-3.8) (base) william@winLab:~/proj/xplot$ pdm info
PDM version:
  2.15.4
Python Interpreter:
  /home/william/proj/xplot/.venv/bin/python (3.10)
Project Root:
  /home/william/proj/xplot

如果需要将项目默认环境转换为 test3.8,使用 pdm use 命令,然后重新安装所需依赖。安装依赖时很有可能报错,可能需要 pdm remove 依赖,重新 pdm add

pdm use --venv test3.8
pdm instll

但是,对同一个项目切换 python 版本是很少见的需求,一般情况不推荐这么做。更常见的需求是开发 python 包时的多版本环境测试,此时更推荐使用 Nox

结语

以上我们梳理了 pdm 为 Python 使用带来的便利。当你需要开发自己的 Python 包时,pdm 的好处会得到淋漓尽致的体现——是的,你可以完全抛弃 poetry 等传统的开发工具了
但即使你无心开发 Python 包,pdm 也会使你的 Python 项目结构更加合理、依赖更加清晰,使项目在机器间迁移和扩展的成本降低
玩的开心!