來了!劃時(shí)代的 Python 包管理工具 -- PDM
在剛聽到 PDM 時(shí),我下意識認(rèn)為它是 Python Development Manager,又一個和 Pipenv 和 Poetry 一樣換湯不換藥的虛擬環(huán)境管理工具。
一直到我翻到了作者的博客,才知道 PDM 的全稱是 Python Development Master,比我想像的還要牛逼一個檔次。
值得一提的是,PDM 的作者是 PyPa 成員、Pipenv 目前主要的維護(hù)者之一,最重要的是,他是中國人,因此這是一款國人開發(fā)的工具。
# 1. Why PDM?
早期的包管理器(如 Pipevn,Poetry),都是基于虛擬環(huán)境的,虛擬環(huán)境主要是為了隔離項(xiàng)目開發(fā)環(huán)境,但如果涉及到虛擬 環(huán)境嵌套虛擬環(huán)境,問題就難搞了,經(jīng)常會出現(xiàn)問題。
PDM 得益于一個 2018 年的 PEP 提案(PEP582,Python local packages directory),完全摒棄了虛擬環(huán)境。
從作者的博客上來看,當(dāng)初之所以要重復(fù)造個輪子,完全是因?yàn)?Pipenv 和 Poetry 都不夠好用,正好有 PEP582 ,可以開發(fā)一個劃時(shí)代的 Python 包管理工具,它就是 PDM 。
PDM 包含如下特性:
PEP 582 本地項(xiàng)目庫目錄,支持安裝與運(yùn)行命令,完全不需要虛擬環(huán)境。
一個簡單且相對快速的依賴解析器,特別是對于大的二進(jìn)制包發(fā)布。
兼容 PEP 517 的構(gòu)建后端,用于構(gòu)建發(fā)布包(源碼格式與 wheel 格式)
擁有靈活且強(qiáng)大的插件系統(tǒng)(有插件系統(tǒng)直接就拉開一個檔次)
PEP 621 元數(shù)據(jù)格式
像 pnpm 一樣的中心化安裝緩存,節(jié)省磁盤空間
盡管 PDM 是國人開發(fā),但考慮到國際化,官網(wǎng)文檔是全英文的。
我花了整整一天,通讀完文檔,消化了 70% 的 PDM 用法,現(xiàn)將心得整理分享出來,會對你上手 PDM 有幫助。
關(guān)于 PDM,內(nèi)容挺多的,打算分兩篇文章來完整地介紹它:
面向新手的入門級教程
面向骨灰級選手的教程
本篇是第一篇,先讓大家對 pdm 的基本用法有一個框架性的理解,而 pdm 真正競爭力請持續(xù)關(guān)注后續(xù)文章。
# 2. 安裝 PDM
PDM 的安裝方法有很多種,在官網(wǎng)上就有 6 種,比如 pip、pipx、homebrew 等
在以前的文章中,我推薦過 pipx 工具,在安裝那種命令行應(yīng)用的包時(shí)非常好用。
而此時(shí) PDM 就是一個命令行工具,因此我也推薦使用 pipx 安裝,方便統(tǒng)一對命令行進(jìn)行管理
執(zhí)行 pipx install pdm 即可安裝

PDM 只有 Python 3.7+ 的版本才能使用,使用其他的方法安裝,要先保證你的 Python 版本,但使用 pipx 則不需要你去操心。
# 3. 初始化 PDM
執(zhí)行 pdm init 就會開始初始化,初始化的時(shí)候,會讓你選擇項(xiàng)目的一些信息
是否要上傳 PyPI
依賴的 Python 版本
License 類型
作者信息
郵箱信息
我機(jī)器上有 Python ?2.7 和 Python 3.10 兩個版本,在初始化項(xiàng)目時(shí)會把機(jī)器上的所有 Python 版本都掃描出來了,會讓選擇項(xiàng)目的 Python 版本。

完成之后,PDM 會將你的選擇以 toml 格式寫入 pyproject.toml 配置文件中。

# 4. PDM 用法
pdm 有非常多的命令,使用 -h 可以看到幫助菜單

?4.1 安裝包
和 Poetry 一樣,安裝使用的是 add 命令,但 pdm 的 add 比 poetry 好用,主要體現(xiàn)在分組,具體請關(guān)注后續(xù)文章

?4.2 查看包
使用 pdm list 可以以列表形式列出當(dāng)前環(huán)境已安裝的包

再加個 --graph 就能以樹狀形式查看,直接依賴包和間接依賴包關(guān)系的層級一目了然

pdm list 還有兩個選項(xiàng):
--freeze:以 requirements.txt 的格式列出已安裝的包
--json:以 json 的格式列出已安裝的包,但必須與 --graph 同時(shí)使用
要查看某個包的某體詳情,直接用 pdm show 即可

?4.3 刪除包
刪除包使用的是 remove 命令

?4.4 項(xiàng)目配置
不加任何參數(shù),可以打印出該項(xiàng)目的環(huán)境配置

想要修改的話,只要加 key 和 value 做為參數(shù)即可,以修改 pypi 鏡像代理為例
原來上面是豆瓣源,現(xiàn)在我要改成阿里源,只需要執(zhí)行如下命令,可比 poetry 方便多啦~

pdm config 里面有非常多的配置,想要一一搞清楚的可以去官網(wǎng)查閱:https://pdm.fming.dev/configuration/
?4.5 運(yùn)行命令
想要在 pdm 的環(huán)境中執(zhí)行命令或者項(xiàng)目,可以使用 ?run 命令,若是執(zhí)行項(xiàng)目時(shí),有諸多參數(shù),可以在 pyproject.toml 配置命令別名,具體用法,請往后看

?4.6 查看環(huán)境
使用 info 命令,可以查看當(dāng)前項(xiàng)目的環(huán)境信息

?4.7 更新包
更新的話,簡單的場景下,使用下面這兩條即可
#?更新所有包
pdm?update?
#?更新某個包
pdm?update?
復(fù)雜的場景,pdm 也都為你考慮到了,它提供了很多選項(xiàng),可以根據(jù)需要使用(以下如有解釋錯誤,請幫忙指正)
--save-compatible:項(xiàng)目依賴可兼容的版本
--save-wildcard:保存通配符版本(暫不明白)
--save-exact:保存有指定確切版本的包
--save-minimum:保持最小版本的包
--update-reuse:盡量只更新命令行中指定的包,其依賴包能不更新則不更新
--update-eager:更新某個包順帶更新其依賴包(遞歸升級)
--prerelease:允許提前釋放(暫不明白)
--unconstrained:忽略包版本的約束,可將包升級至最新版本
--top:僅更新有在 pyproject.toml 的包
--dry-run:試運(yùn)行,而不去修改 lock 文件
--no-sync:只更新 lock 文件,但不更新包
如果你的依賴包有設(shè)置分組,還可以指定分組進(jìn)行更新
pdm?update?-G?security?-G?http
也可以指定分組更新分組里的某個包
pdm?update?-G?security?cryptography
再加個 -d 就可以再指定 dev 依賴
#?更新所有的?dev?依賴
pdm?update?-d
#?更新?dev?依賴下某個分組的某個包
pdm?update?-dG?test?pytest
同樣地,也可以指定 --prod 或者 ?--production 升級非 dev (即生產(chǎn))的包。
?4.8 切換 py
當(dāng)你在初始化 pdm 項(xiàng)目時(shí),就已經(jīng)選定了當(dāng)前的 Python 版本和可用的 Python 版本范圍,后面如果想更改,可以使用 use 命令,但版本要受之前設(shè)定的版本范圍約束。
假設(shè)允許范圍是 python 3.9+,當(dāng)前使用的是 python 3.10,可以直接切換過去。
pdm?use?python3.9
# 5. 命令別名
在 pyproject.toml 添加 [tool.pdm.scripts] 可以設(shè)置快捷命令別名,若項(xiàng)目的執(zhí)行有非常多的參數(shù),這種設(shè)定別名的方法將很有用。

[tool.pdm.scripts] 有兩種形式
#?第一種
[tool.pdm.scripts]
start?=?"python?main.py"
#?第一種
[tool.pdm.scripts]
start?=?{cmd?=?"python?main.py"}
但若想在參數(shù)中加注釋,就必須得使用第二種方法,例如這樣
[tool.pdm.scripts]
start?=?{cmd?=?[
????"flask",
????"run",
????#?Important?comment?here?about?always?using?port?54321
????"-p",?"54321"
]}
除了 cmd 之外,還有兩個參數(shù)
一個是 shell 參數(shù),從輸出來看你應(yīng)該和看出和 cmd 的區(qū)別,和 subprocess.Popen() with shell=True 差不多一個意思

一個是 env_file 參數(shù),可以指定配置環(huán)境變量的文件
[tool.pdm.scripts]
start.cmd?=?"flask?run?-p?54321"
start.env_file?=?".env"
如果想要把這個環(huán)境變量的文件不僅限于某個命令,而是 pdm run 全局,可以這樣配置
[tool.pdm.scripts]
_.env_file?=?".env"
加 --list 或者 ?-l可以查看所有設(shè)置的快捷別名

對于每一個快捷命令,都可以設(shè)置 pre 和 post 命令:
pre 命令:在每次快捷命令執(zhí)行前會執(zhí)行
post 命令:在每次快捷命令執(zhí)行后會執(zhí)行
[tool.pdm.scripts]
pre_compress?=?"{{?Run?BEFORE?the?`compress`?script?}}"
compress?=?"tar?czvf?compressed.tar.gz?data/"
post_compress?=?"{{?Run?AFTER?the?`compress`?script?}}"
# 6. 自動補(bǔ)全
pdm 的命令雖多,但并不復(fù)雜,并不太需要使用自動補(bǔ)全,若你真的需要補(bǔ)全,也可以實(shí)現(xiàn)。
對于不同的 shell,自動補(bǔ)全的配置方式都不太一樣,這個在官網(wǎng)上有詳細(xì)的說明。
如果你和我一樣使用的 zsh,可以參照我的配置方式。

截圖中間有一步是 vim ~/.zshrc ,是將 pdm 插件配置到 zsh 中
plugins=(git?z?macos?extract?zsh-syntax-highlighting?zsh-autosuggestions?pdm)
# 7. 方案兼容
?其他方案遷移到 pdm
pdm 足夠好用,也足夠開放,如果你當(dāng)前使用的是其他的包管理器,比如 pipenv ,poetry,或者還在用最原始的 requirements.txt ,你也可以很方便的遷移到 pdm 中來:
使用 pdm import -f {file} 無需初始化,直接轉(zhuǎn)換
執(zhí)行 pdm init 或者 ?pdm install 的時(shí)候,會自動識別你當(dāng)前的依賴情況并轉(zhuǎn)換
?pdm 遷移到其他方案
同樣的,你也可以當(dāng) pdm 管理的項(xiàng)目,導(dǎo)出為其他方案
pyproject.toml 和 pdm.lock是 pdm 的兩個核心文件。
pdm 做為一個后起之秀,也沒有忘本,它支持:
將 pyproject.toml 轉(zhuǎn)成 setup.py
pdm?export?-f?setuppy?-o?setup.py將 pdm.lock 轉(zhuǎn)成 requirements.txt
pdm?export?-o?requirements.txt
# 8. 總結(jié)一下
花了很大的力氣,終于把 PDM 的基本用法給介紹完畢,相信一定會有人會提出質(zhì)疑:這就是你所謂的 劃時(shí)代的包管理器 ?
實(shí)際上,上面僅僅是入門操作,而 PDM 的一些核心知識,考慮到篇幅有限,我將這些進(jìn)階類的內(nèi)容安排在后續(xù)文章(請持續(xù)關(guān)注我后續(xù)文章),它將包括但不僅限于:
PDM 的原理剖析:PEP 582 提案
發(fā)布包的構(gòu)建:PEP 517 提案
Hook 腳本的定義與使用
插件管理系統(tǒng)與自定義插件
緩存管理系統(tǒng)的介紹
這些內(nèi)容是 PDM 的核心,只有理解了這些,你才能真正用好 PDM,到那時(shí)你會感慨:為什么 ?Guido 還不把這樣的工具收編成標(biāo)準(zhǔn)的包管理工具?
? ?

