github.com/pdm-project/pdm
pdm.fming.dev
PDMによるPythonのプロジェクト管理
project.toml
など PEP 最新に準拠した python の package manager- python v3.7 以上で利用可能
- poetry より依存解決速いらしい
- npm をもじってるだけあって task runner なども標準で備わってる
.venv
依存だが内部で package を cache するため軽量 & install 速い- pypi 用の package を作る project のために build, publish にも対応
- Build and Publish
- Dynamic Versioning もできて package 制作に便利そう
- reject された pep 582 に触発された package manager らしい
- PEP 582 - peps.python.org/pep-0582
- PEP 582 (pypackages) がRejectされました
- ざっくり
node_modules
やろうよという提案、一旦は棄却されている - pdm で PEP 582 の使用 が推奨されてないが一応できるみたい
相変わらず python の package 管理は地獄な中、わりとモダンな pep 582 の考え方から生まれていて、npm っぽいコマンド体系を持ってる。
pdm は仮想環境 .venv
について、その管理と透過的なコマンド実行 (package add, remove, install, run など) を行ってくれるため、venv.backend に PEP 標準の venv を使い、以降は pdm 経由で package の管理や実行をすることで activate / deactivate を省略しつつ仮想環境を運用する のが、標準準拠で楽そうに見えてる。
# macOS
$ brew install pdm
# or Install via installer script
$ curl -sSL https://raw.githubusercontent.com/pdm-project/pdm/main/install-pdm.py | python3 -
$ echo 'export PATH=$HOME/.local/bin:$PATH' >> ~/.zshrc
$ exec $SHELL -l
本番環境で pip 使えるなら pip が、パス解決が面倒なら pipx がラク。
# Install via pip
$ pip install --user pdm
$ exec $SHELL -l
# Install via pipx
$ python3 -m pip install --user pipx
$ python3 -m pipx ensurepath
$ exec $SHELL -l
$ pipx install pdm
動作確認と個人的な初期設定 (任意) 。
# Check Version
$ pdm --version
> 2.6.1
# Update self
$ pdm self update
# To enable install cache
$ pdm config --global install.cache True
# Switch venv backend to official venv
$ pdm config --global venv.backend venv
# Using PEP 582
$ pdm --pep582
init 時に勝手に作ってくれるけど、既存プロジェクトに後から入れるとかで、あえて pdm 関連だけ抜き出すとこんな感じかな。
# pdm
.pdm.toml
.pdm-python
__pypackages__/
- 前提、公式の python extension が必要
- 当該プロジェクトを開いたら
Python: Select interpreter
で.venv
を選択 - 以降は linter とかうまく動いてくれるはず
プロジェクトの初期化で pyproject.toml を対話で作ってくれる。以下のことに応える感じ。
- python 実体 (pyenv など) => 利用する python version や venv の作成先に利用
- .venv 作るかどうか => 基本作る一択だと思う
- 1 の python cli を利用する前提の
.venv
を作成してくれる - 以降はその
.venv
に実際に依存を入れていく感じ
- 1 の python cli を利用する前提の
- ライブラリか否か (pypi 登録する系の project か) => default No なのでお好みで
$ pdm init
npm 的な感覚で package add, remove してくと pyproject.toml
と pdm.lock
が更新されていく。
# add
$ pdm add requests # add requests
$ pdm add requests==2.25.1 # add requests with version constraint
$ pdm add requests[socks] # add requests with extra dependency
$ pdm add "git+https://github.com/pypa/pip.git@22.0" # Install pip repo on tag `22.0`
# add dev dependency
pdm add -d flake8 mypy black isort pytest-cov
# remove
$ pdm remove requests
- install 系も npm ぽい、更新しながら
pdm install
と、lock 通りに入れる ci 用のpdm sync
- package の version up & toml と lock の更新も
pdm update
とかで ok .venv
がない (= clone してきた直後状態など) ではこのタイミングで自動的に.venv
の作成までやってくれる- ここで選択される python は自動選択されるっぽい?(未検証)
- 手動選択したければ事前に
pdm use
で「 venv 作成・操作に使うメインの interpreter 」を選択するといいのかな
# install from pyproject.toml with update lock
$ pdm install
# installs packages from the lock file
$ pdm sync
# will update the lock file, then sync
$ pdm update
pip install
とは違い venv に package を install することになる- そのため本番環境で
pdm sync
などを使う場合は 必ずエントリーポイントを pdm script 経由など、venv が activate される状態で開始しなければならないことに注意 - または
export
でrequirements.txt
作ることも可能
$ pdm export -o requirements.txt
npm scripts みたいに add した package に関する shell script を実行できる。
$ pdm add flask
$ pdm run flask run -p 54321
run
に何も引数を渡さないと python interpreter の repl になる。
$ pdm run
> Python 3.x.x ...
> Type "help", "copyright", "credits" or "license" for more information.
>>>
# pyproject.toml
[tool.pdm.scripts]
start = "flask run -p 54321"
$ pdm run start
> Flask server started at http://127.0.0.1:54321
[tool.pdm.scripts]
filter_error = {shell = "cat error.log|grep CRITICAL > critical.log"}
# main.py
def helloworld(name):
print(f"hello world {name}!")
[tool.pdm.scripts]
helloworld = {call = "main:helloworld(sys.argv[-1])"}
$ pdm run helloworld bob #=> hello world bob!
使ってないので調べてないけど、pypi への publish とその準備用 build コマンドもある。
# Build & Publish
$ pdm publish
# Build and publish separately
$ pdm build
$ pdm publish --no-build
npm の node_modules
みたいなパッケージ管理ができるやつ。PEP 582 拒否されて非推奨ではあるが、現時点で python のパッケージ管理 & 仮想化まわりは標準があってないようなものなので、あんまり気にしてない。(pip, venv 以外はどれも同じくらい不安定)
- PEP 582 機能
__pypackages__
を使う設定が global or project で構成可能 - PEP 582 を有効にすると
pdm add
で__pypackages__
に依存が入る - vscode で上記を
.vscode/settings.json
などで読み込んでやれば linter 解決できる - 格納される packages は python interpreter との紐づきはないため pyenv などで別途 python version 管理が必要
lambda の container image 開発 など、完全に docker で完結させる (local 実行をしない) 場合、仮想環境自体が不要 & vscode など editor 側で依存解決ができれば十分なケースでは有用に思う。
逆に local ではこれを使う利点はほぼないと思うので、以下 docker setup 時の構成だけメモする。
ざっと調べた感じ python.use_venv false
である or __pypackages__
が存在すると PEP 582 プロジェクトとして認識されるよう。後者は ignore されるはずなので前者で構成し pdm.toml
(pdm project setting file) で明示してあげる感じぽい。
# pdm.toml (git 管理) に仮想環境使わないよ設定 (pep 582 使うよ設定) が追加される
$ pdm config -l python.use_venv false
# pdm.toml (当然、このファイル置いとくだけでもよき)
[python]
use_venv = false
# ↑ pdm.toml 見て init 時に .venv が生成されなくなる
$ pdm init
# add, remove, sync なども __pypackages__ 依存になる
$ pdm add numpy
例えば lambda の python image を使った開発で、container 内で生成した __pypackages__
を host os の vscode に読ませるような構成なら。
# https://hub.docker.com/r/amazon/aws-lambda-python
FROM public.ecr.aws/lambda/python:3.10 as base
COPY ./ ./
RUN pip install pdm # pdm いれる
# docker-compose x watchmedo で hot reload 開発用の stage を構成
#
# awslambdaric を含む依存を compose up 後に pdm sync することで
# __pypackages__ を host os に反映して vscode から読み取る想定で組まれている
#
FROM base as docker-compose
RUN pip install "watchdog[watchmedo]"
ENTRYPOINT ["watchmedo", "auto-restart", "-d", ".", "-p", "*.py", "--", "aws-lambda-rie", "pdm", "run", "python", "-m", "awslambdaric"]
CMD ["app.lambda_handler"]
# sam local でのテストや本番 deploy 用の stage を構成
#
# 本番も pdm sync にしてもいいけど、そうすると package 解決のために pdm run 経由で
# entrypoint を組む必要があるため、シンプルに global に pip install させる
#
FROM base as main
RUN pdm export -o requirements.txt # pdm.lock から requirements.txt 生成
RUN pip install -r requirements.txt
CMD ["app.lambda_handler"]
↑ みたいに docker 組んで docker-compose
stage で docker-compose から build => up して、bash で入って python.use_env false
な (= PEP 582 が有効な) 状態の pdm で pdm init
や pdm add
していく。
docker-compose.yml
はこんな感じ。(sam local 実行では、都度 build して変更を取り込むので、ここでは特に触れない)
# docker-compose.yml
services:
lambda:
ports:
- "8080:8080"
build:
context: ./lambda/src
dockerfile: Dockerfile
target: docker-compose
volumes:
- ./lambda/src:/var/task
# __pypackages__ は volume trick せず build => up 以降に sync して
# host 側に持ってこさせて vscode など editor に依存を読み込ませる想定
dip で開発用のコマンド類や、開発用の provisioning script を組むならこう。
# dip.yml
interaction:
lambda:
service: lambda
compose:
# lambda 用 image は entrypoint を override しないと bash で入れない
run_options: [tty, interactive, rm, entrypoint bash]
pdm:
service: lambda
compose:
run_options: [tty, interactive, rm, entrypoint pdm]
provision:
- dip down
- dip compose build
- dip pdm sync
- dip down
運用としては以下のような感じ。
# setup
$ git clone ...
$ dip provision # container build => up => pdm sync して __pypackages__ を local に
# repl console
$ dip pdm run
# add, remove package
$ dip pdm add numpy
$ dip pdm remove numpy
# re-install packages
$ dip pdm sync
最後に、vscode は python extension と、project 設定として .vscode/settings.json
を以下のようにしてやれば、勝手に __pypackages__
配下に依存を解決しにいってくれる。 __pypackages__/
から /lib
までのパスは image や python version によるので bash で入って確認してやる。
{
// for python extensions
"python.autoComplete.extraPaths": [
"./lambda/src/__pypackages__/3.10/lib"
],
// for pylance
"python.analysis.extraPaths": [
"./lambda/src/__pypackages__/3.10/lib"
]
}
最近
pdm
を使い始めた者です。初心者で申し訳ないのですが、
.venv
を使うのと__pypackages__
を使うのとではどう違いますか?あるいはそれぞれの長所短所を教えて頂くことはできますか?