PDMによるPythonのプロジェクト管理
PDMとは
PDMはPythonのプロジェクト、依存関係のマネージメントツールです。私は以前までPoetryを利用していましたが以下の点からPDMに乗り換えました。
-
pyproject.toml
を利用する点はPoetryと同じだが、よりPEP準拠になっている。 - Poetryよりも依存解決が速い
- Poetryはいつまで経ってもv1.2がリリースされない。
- 単体でタスクランナー機能を有している。(Poetryはプラグインが必要)
- 指定したファイルの
__version__
を読み込んでくれる。 - プロジェクトごとにvenvを作るが、インストールされるライブラリは中央管理されていてシンボリックリンクを張ることでディスク容量を節約でき、インストールも速い。
特に私が一番気に入っているのは最後のインストールキャッシュ機能で、例えばDebian 11にてPoetryでNumPyだけをインストールした場合、.venv
のサイズは88MBですが、PDMの場合はなんとわずか176KBです。これはPDMはライブラリの実体を$HOME/.cache/pdm/packages
以下にインストールし、.venv/lib/python<version>/site-packages
内にリンクを張っているからです。したがってPoetryの場合はNumPyを利用する各プロジェクトごとに88MB消費しますが、PDMの場合はいくらプロジェクトを増やしても同じバージョンであれば合計88MBで済みます。
インストール
Debian 11ではpipxを利用するのが一番簡単です。
sudo apt install pipx
pipx install pdm
そして忘れずに
pdm config --global install.cache True
を実行して前述のインストールキャッシュ機能を有効にします。(なぜかこれはデフォルトではFalse)
使い方
# add dependency
pdm add numpy
# add optional dependency
pdm add -G <group_name> scipy
# add dev dependency
pdm add -d flake8 mypy black isort pytest-cov
# install from pyproject.toml
pdm install
# without dev dependency
pdm install --prod
# with optional dependency
pdm install -G <group_name>
# run with installed dependency
pdm run <cmd>
# build wheel
pdm build
なお、プロジェクトのメインのソースコードはsrc/
以下に入れておくのがデフォルトのしきたりになっています。
また、pdm install/sync
はプロジェクト自体はsetup.py develop
相当になっていて、setup.py install
相当にするにはpdm install/sync --no-editable
にします。
pyproject.tomlの設定
pdm add
コマンド以外で手動で編集するべき項目。詳細は
を参照。
dynamic versioning
[project]
dynamic = ["version"]
[tool.pdm]
version = { source = "file", path = "src/app/version.py" }
これでsrc/app/version.py
内の__version__
を読んでくれます。
プロジェクトスクリプト
[project.scripts]
app = "app.cli:main"
これでpdm install
したときに.venv/bin/app
が作成され、実際にはsrc/app/cli.py
の中のmain
という名前の関数が実行されます。
タスクランナー
black = "black ."
isort = "isort ."
flake8 = "flake8 --exit-zero ."
mypy = "mypy --show-column-numbers ."
test = "pytest tests --cov=app --cov-report=term --cov-report=xml"
format = { composite = ["black", "isort"] }
lint = { composite = ["flake8", "mypy"] }
check = { composite = ["format", "lint", "test"] }
上記のように書くとpdm run check
でblackとisortとflake8とmypyとpytestをすべてやってくれます。
Dockerfileの書き方
をベースに少しいじっています。
FROM python:3.10-slim as pdm
RUN pip install -U pip setuptools wheel
RUN pip install pdm
# PDM 2.0からはデフォルトでvenvを使うのでoffにしてpep 582モードにする
RUN pdm config python.use_venv False
FROM pdm as builder
WORKDIR /project
# 一回目は依存ライブラリのみをインストール
COPY pyproject.toml pdm.lock /project/
RUN pdm sync -G server --prod --no-self
# 二回目はプロジェクト本体をインストール
# 分けることによって依存ライブラリのインストールをDockerのビルドキャッシュに載せることができる
COPY src/ /project/src
RUN pdm sync -G server --prod --no-editable
FROM python:3.10-slim
ENV PYTHONPATH=/project/lib
COPY /project/__pypackages__/3.10/lib /project/lib
COPY /project/__pypackages__/3.10/bin /project/bin
WORKDIR /project
COPY src/ /project/src
ENV N_WORKERS 4
CMD bin/gunicorn \
--access-logfile - \
--bind=0.0.0.0:8000 \
--workers=${N_WORKERS} \
-k uvicorn.workers.UvicornWorker \
src.app.server:app
まとめ
にテンプレートとして使えるプロジェクトを作成しました。上記の内容以外にlinterやformatterの設定をsetup.cfg
にまとめてあり、またVSCodeの設定もvscode_templates
に入れてあるのでこれを.vscode
にコピーし、extensinos.json
に挙げられている拡張をインストールするとすぐに快適な開発環境になります。
Discussion