PDM - Python Development Master 【翻訳】
はじめに
PEP 582 に対応した Python のパッケージマネージャは Pyflow くらいしか知りませんでした。Pyflow は Rust 製だし〜という理由からか、使用しているという声をあまり聞きませんでしたが、PDM という Python 製の PEP 582 に対応したパッケージマネージャを偶然見つけたのでドキュメントを和訳してみました。現状、Poetry でいいとは思うのですが、PEP 582 も体験してみたいという方のご参考になれば。
PDM はコミュニティードリブンを目指しているので、盛り上がるといいですね。
(なお私は和訳しただけでまだ使っていません)
PDM - Python Development Master
イントロダクション
PDMは PEP 582 をサポートしたモダンなパッケージマネージャです。vertualenv を作成せずに、npm
と似たような方法でパッケージをインストール、管理することが可能です。
機能のハイライト
- virtualenv を必要としない方法であるPEP 582 [1]を採用したローカルパッケージインストーラとランナー
- 主に巨大バイナリ配布パッケージに対するシンプルで比較的高速な依存リゾルバ
- PEP 517 [2]準拠のビルドバックエンド
- PEP 621 [3]準拠のプロジェクトメタデータ
インストール
PDM のインストールには Python 3.7+ がインストールされていることが必要です。PDM はマルチプラットフォーム対応で、 Windows, Linux, MacOS で動作します。
おすすめのインストール方法
MacOS で homebrew
を使用しているなら、
$ brew install pdm
そうでなければ、システムの Python 環境がめちゃくちゃになることを避けるために、pipx[4]で PDM をインストールすることをお勧めします。
$ pipx install pdm
他のインストール方法
ユーザー環境に PDM を pip
でインストールするなら、
$ pip install --user pdm
PEP 582 をグローバルで有効化
Python インタプリタに PEP 582 パッケージを認識させるには、pdm/pep582/sitecustomize.py
を Python のライブラリ検索パスに追加する必要があります。
Windows ユーザー
pdm --pep582
とすれば、環境変数が自動で変更されます。変更を有効化するために、ターミナルセッションを再起動するのを忘れないでください。
Mac, Linux ユーザー
環境変数を変更するコマンドは、pdm --pep582 [<SHELL>]
です。<SHELL>
が与えられない場合、PDM はシェルを推定します。
ログイン時に有効化するには、.bash_profile
(あるいは別のシェルのプロファイル) に設定を記述します。例えば、
$ pdm --pep582 >> ~/.bash_profile
有効化にはターミナルセッションの再起動が必要です。
docker イメージを使う
PDM はデプロイフローを簡単にするために、docker イメージを提供しています。使用するには、Dockerfile に下記のように記述してください。
FROM frostming/pdm
COPY . /app
# -- Replace with the correct path to your app's main executable
CMD ["pdm", "run", "python", "main.py"]
シェル補完
PDM は Bash, Zsh, Fish, Powershell 用の補完生成スクリプトを提供しています。各シェルの一般的な場所は以下のようになっています。
# Bash
$ pdm completion bash > /etc/bash_completion.d/pdm.bash-completion
# Zsh
# Make sure ~/.zfunc is added to fpath, before compinit.
$ pdm completion zsh > ~/.zfunc/_pdm
# Oh-My-Zsh
$ mkdir $ZSH_CUSTOM/plugins/pdm
$ pdm completion zsh > $ZSH_CUSTOM/plugins/pdm/_pdm
# Then make sure pdm plugin is enabled in ~/.zshrc
# Fish
$ pdm completion fish > ~/.config/fish/completions/pdm.fish
# Powershell
# Create a directory to store completion scripts
PS > mkdir $PROFILE\..\Completions
PS > echo @'
Get-ChildItem "$PROFILE\..\Completions\" | ForEach-Object {
. $_.FullName
}
'@ | Out-File -Append -Encoding utf8 $PROFILE
# Generate script
PS > Set-ExecutionPolicy Unrestricted -Scope CurrentUser
PS > pdm completion powershell | Out-File -Encoding utf8 $PROFILE\..\Completions\pdm_completion.ps1
Unicode と ANSI サポート
ANSI 文字と Unicode 絵文字によって、豪華なターミナル UI を提供します。使用しているターミナルのサポート状況により、自動でオンオフを切り替えることができます。この機能をオフにするには環境変数 DISABLE_UNICODE_OUTPUT=1
としてください。
IDE で使う
今のところ、ほとんどの IDE は PEP 582 に対応したビルトインプラグインをサポートしていません。そのため、手動で設定する必要があります。
PDM はプロジェクトの設定を .pdm.toml
に保存するので、これを git の管理から外すために .gitignore
下記のように記入することをお勧めします。
.pdm.toml
__pypackages__/
PyCharm
__pypackages__/<major.minor>/lib
をSources Root とします。
また、プロジェクト環境にインストールしたツール (e.g. pytest
) を使用する場合、対応する実行/デバッグ構成の環境変数 PATH
に、__pypackages__/<major.minor>/bin
を追加する必要があります。
VSCode
以下を setting.json
に追加します。
{
...
"python.autoComplete.extraPaths": ["__pypackages__/<major.minor>/lib"],
"python.analysis.extraPaths": ["__pypackages__/<major.minor>/lib"]
}
Task Provider
さらに、PDM の VSCode Task Provider extension がダウンロード可能です。
これをインストールすると、VSCode は自動的にpdm scripts を検知できるようになり、VSCode Tasks としてネイティブに実行できます。
依存関係を管理する
PDM はプロジェクトのとその依存関係の管理を助けるコマンド群を提供します。以下の例は Ubuntu 18.04 で実行しており、Windows をお使いの場合、多少の変更が必要となります。
プロジェクトの初期化
$ mkdir pdm-test && cd pdm-test
$ pdm init
PDM が表示するいくつかの質問に答えると、pyproject.toml
がプロジェクトルートに生成されます。
[project]
name = "pdm-test"
version = "0.0.0"
description = ""
authors = [
{name = "Frost Ming", email = "mianghong@gmail.com"}
]
license = {text = "MIT"}
requires-python = ">=3.7"
dependencies = []
dev-dependencies = []
もし、pyproject.toml
がすでに存在していたら、メタデータとともにアップデートされます。メタデータのフォーマットはPEP 621 specification に従っています。
それぞれのフィールドの詳細については、Project File を参照してください。
依存関係を追加する
$ pdm add requests
$ pdm add -d pytest
pdm add
コマンドの後には複数の依存関係を入力することができ、依存関係の仕様は PEP 508 で説明されています。依存関係には 2 パターンあり、デフォルトではproject.dependencies
に、pdm add
に -d/--dev
オプションが渡されるとproject.dev-dependencies
に追加されます。
また、PDM では -s/--section <name>
オプションを指定することで追加の依存関係グループを作れ、その依存関係はそれぞれプロジェクファイルのproject.optional-dependencies.<name>
に追加されます。
その後、依存関係やサブ依存関係は適切に解決、インストールされます。全ての依存関係の解決結果は、pdm.loc
で確認できます。
ローカルパッケージは pdm add -e/--editable <local project path>
を使うことで editable mode (pip install -e <local project path>
のように) インストール可能です。
バージョン指定子を保存する
pdm add requests
のように、パッケージのバージョンが指定されない場合、3 つのバージョン指定の方法を提供します。指定には、--save-<strategy>
を使用します(下記の例では2.21.0
が依存関係の最新バージョンとします)。
-
compatible
: compatible なバージョン指定子を保存します:>= 2.21.0,<3.0.0
(デフォルト) -
exact
: 正確なバージョン指定子を保存します:==2.21.0
-
wildcard
: バージョン制約を外し、指定子をワイルドカードのままにします:*
依存関係をアップデートする
ロックファイルに存在する全ての依存関係をアップデートするには、
$ pdm update
特定のパッケージをアップデートするには、
$ pdm update requests
アップデート形式について
同様に、PDM は、依存関係とサブ依存関係のアップデートに 2 つの異なる挙動を提供します。これは、--update-<strategy>
オプションで指定します。
-
reuse
: コマンドラインで指定された物を除いて、全てのロックされた依存関係を保持します -
eager
: コマンドラインで指定されたパッケージの新しいバージョンとその再帰的なサブ依存関係をロックし、それ以外の依存関係は保持します
依存関係の削除
プロジェクトファイルとライブラリのディレクトリに存在する依存関係を削除するには、
$ pdm remove requests
プロジェクトパッケージをロックファイルと同期する
2 つの似た動作をするコマンドがありますが、わずかに異なります。
-
pdm install
はロックファイルをチェックし、プロジェクトファイルと一致しない場合は再ロックを行い、パッケージをインストールします。 -
pdm sync
はロックファイルの依存関係をインストールし、ロックファイルが存在しなければエラーを吐きます。さらに、--clean
オプションが指定された場合、不要なパッケージを削除します。
インストールしたパッケージを表示する
pip list
のように、インストールされている全てのパッケージを一覧表示できます。
$ pdm list
また、依存関係のグラフを表示できます。
$ pdm list --graph
tempenv 0.0.0
└── click 7.0 [ required: <7.0.0,>=6.7 ]
black 19.10b0
├── appdirs 1.4.3 [ required: Any ]
├── attrs 19.3.0 [ required: >=18.1.0 ]
├── click 7.0 [ required: >=6.5 ]
├── pathspec 0.7.0 [ required: <1,>=0.6 ]
├── regex 2020.2.20 [ required: Any ]
├── toml 0.10.0 [ required: >=0.9.4 ]
└── typed-ast 1.4.1 [ required: >=1.4.0 ]
bump2version 1.0.0
PyPI インデックス URL を設定する
PyPI ミラー URL を以下のように設定できます。
$ pdm config set pypi.url https://testpypi.org/simple
デフォルトでは、PyPI URL を pip の設定ファイルから読み込みますが、見つからない場合は https://pypi.org/simple
に設定します。
パッケージのソースを追加する
PyPI もしくはそのミラーよりプライベートなリポジトリからパッケージをインストールしたい場合には、その場所を pyproject.toml
に保存し、デプロイ時にプロジェクトと一緒に同梱する必要があります。
[[tool.pdm.source]]
url = "http://example.com/private/index"
verify_ssl = false # Don't verify SSL, it is required when you are using `HTTP` or the certificate is trusted.
name = "private"
プレリリースバージョンのインストールを許可する
プレリリースバージョンのインストールを許可するには、以下の設定を pyproject.toml
に保存します。
[[tool.pdm]]
allow_prereleases = true
環境変数の展開
PDM は、依存関係の指定のため環境変数の展開をサポートします。
-
URL の認証部分の環境変数が展開されます:
https://${USERNAME}:${PASSWORD}/artifacts.io/Flask-1.1.2.tar.gz
. URL に直接 auth 部分を指定しなくても問題ありません。-v/--vervose
オプションが指定されていれば、PDM は auth 部分を要求します。 -
${PROJECT_ROOT}
は、POSIX スタイル(i.e. 前方スラッシュ/
, Windows でも) でプロジェクトルートの絶対パスに拡張されます。一貫性のため、${PROJECT_ROOT}
の下のローカルパスを参照する URL はfile:///
(3 つのスラッシュ) で始まる必要があります。e.g.file:///${PROJECT_ROOT}/artifacts/Flask-1.1.2.tar.gz
.
資格情報の漏洩について心配する必要はありません。環境変数は必要に応じて展開され、ロックファイルでは変更されません。
プロジェクトの管理
PDM は PEP 517 ビルドバックエンドとして動くことができます。有効化するには、下記のように pyproject.toml
に追記してください。プロジェクト作成時に pdm init
を使った場合にはデフォルトで書き込まれています。
[build-system]
requires = ["pdm-pep517"]
build-backend = "pdm.pep517.api"
pip
コマンドはバックエンド設定を読み込み、パッケージのインストールやビルドを行います。
Python インタープリタを選ぶ
pdm init
を使った場合、PDM がどうやって Python インタープリタを検出し選択するか、すでにご存知のはずです。初期化後、pdm use <python_version_or_path>
で設定を変更することもできます。この引数には任意の長さのバージョン指定子か、python インタープリタの相対あるいは絶対パスを指定できますが、Python インタープリタはプロジェクトファイル中の python_requires
制約に適合していなければいけません。
requires-pytyhon
によるプロジェクトのコントロール
PDM は requires-python
で指定した値に含まれる全てのバージョンの Python で動作するパッケージ候補を選択しようとします。例えば requires-python
の値が >= 2.7
であったら、PDM はバージョン範囲が >= 2.7
のスーパーセットであるパッケージ foo
の最新バージョンを探そうとします。
そのため、古いバージョンパッケージでロックされたくないなら、requires-python
を適切に指定してください。
配布アーティファクトをビルドする
$ pdm build
- Building sdist...
- Built pdm-test-0.0.0.tar.gz
- Building wheel...
- Built pdm_test-0.0.0-py3-none-any.whl
アーティファクトはtwine によってPyPI にアップロードできます。pdm build --help
で利用可能なオプションを確認できます。
現在の Python 環境を表示する
$ pdm info
Python Interpreter: D:/Programs/Python/Python38/python.exe (3.8.0)
Project Root: D:/Workspace/pdm
[10:42]
$ pdm info --env
{
"implementation_name": "cpython",
"implementation_version": "3.8.0",
"os_name": "nt",
"platform_machine": "AMD64",
"platform_release": "10",
"platform_system": "Windows",
"platform_version": "10.0.18362",
"python_full_version": "3.8.0",
"platform_python_implementaiton": "CPython",
"python_version": "3.8",
"sys_platform": "win32"
}
プロジェクトを設定する
PDM の comfig
コマンドは、git config
のように動作します。ただし、設定の表示に --list
は必要ありません。
現在の設定を表示するには、
$ pdm config
設定のうち一つを表示するには、
$ pdm config pypi.url
設定値を変更し、ホームの設定ファイルに保存するには、
$ pdm config pypi.url "https://testpypi.org/simple"
デフォルトでは、設定値はグローバルに設定されてしまうため、プロジェクトオンリーで設定するには --local
フラグを追加します。
$ pdm config --local pypi.url "https://testpypi.org/simple"
ローカルな設定はプロジェクトルートディレクトリの .pdm.toml
ファイルに保存されます。
設定ファイルは以下の順番で検索されます。
-
<PROJECT_ROOT>/.pdm.toml
- プロジェクトの設定 -
~/.pdm/config.toml
- ホームの(グローバルな)設定
-g/--global
オプションが使われた場合、最初に読み込まれる設定ファイルは ~/.pdm/global-project/.pdm.toml
に置換されます。
全ての利用可能な設定は、Configration Page を参照してください。
グローバルなプロジェクトを管理する
グローバルな Python インタープリタの依存関係を追跡したい場合でも、PDM を -g/--global
オプションとともに使えば簡単です。-g/--global
オプションはほとんどのサブコマンドをサポートしています。
オプションが渡されると、~/.pdm/global-project
がプロジェクトのディレクトリとして使用されます。このディレクトリはほとんど通常のプロジェクトと同じですが、pyproject.toml
は自動で作成され、ビルド機能はサポートされません。このアイデアは、Haskell の stack から拝借しました。
しかし、stack とは異なり、PDM はデフォルトでは、ローカルプロジェクトが見つからない場合でもグローバルプロジェクトを使用しないようにしています。パッケージが間違った場所に配置されるとあまりよろしくないためです。-g/--global
オプションを明示的に指定すれば、この機能を有効化できます。
もし、グローバルプロジェクトを ~/.pdm/global-project
ではなく別のプロジェクトファイルで追跡したい場合、-g/--global
の後に、別のプロジェクトパスを指定します。
virtualenv とともに使う
PDM はデフォルトでは PEP 582 をオススメしていますが、virtualenv 下にパッケージをインストールすることもできます。use_venv
を True
に設定することで、下記条件の時、PDM は virtualenv を使用します。
- virtualenv がすでに有効化されている
- 有効な virtualenv のフォルダ が
venv
,.venv
,env
のいずれか
また、use-venv
がオンで、インタープリタのパスが venv-like なパスであった場合、PDM はその venv ディレクトリを再利用します。
既存のプロジェクトファイルから、プロジェクトメタデータをインポートする
Pipenv や Poetry といった他のパッケージマネージャを使っていた場合、PDM に移行するのは簡単です。PDM は import
コマンドを用意しており、手動でプロジェクトを初期化することはありません。現在のサポートは、
- Pipenv の
Pipfile
- Poetry の
pyproject.toml
のセクション - Flit の
pyproject.toml
のセクション - Pip で使われる
requirements.txt
また、PDM プロジェクトがまだ初期化されていない場合、pdm init
や pdm install
を実行する際に、PDM はインポート可能なファイルを自動検出できます。
ロックされたパッケージを別のフォーマットにエクスポートする
CI フローやイメージ構築プロセスを簡略化するため、pdm.lock
をエクスポートすることもできます。現在は、requirements.txt
と setup.py
フォーマットのみをサポートしています。
$ pdm export -o requirements.txt
$ pdm export -f setuppy -o setup.py
資格情報を pyproject.toml から隠す
PyPI サーバへのログイン認証や、VCS[5] リポジトリのユーザー名やパスワードといった機微情報を使用する時が多々ありますが、そういった情報を pyproject.toml
中で公開したり git にアップロードすることは避ける必要があります。
PDM はこれを実現するために、いくつかの方法を用意しています。
-
認証情報を環境変数に格納しておき、URL に展開する
[[tool.pdm.source]] url = "http://${INDEX_USER}:${INDEX_PASSWD}@test.pypi.org/simple" name = "test" verify_ssl = false [project] dependencies = [ "mypackage @ git+http://${VCS_USER}:${VCS_PASSWD}@test.git.com/test/mypackage.git@master" ]
環境変数は
${ENV_NAME}
という形式でエンコードされなくてはいけません。他の形式はサポートされておりません。また、auth 部分のみが展開されます。 -
PDM が
-v/--verbose
付きで実行されていた場合に、認証情報が URL で与えられず、サーバが 401 レスポンスを返したならば、PDM はユーザー名とパスワードの入力を要求します。そうでない場合、何が起こったかを示すエラーを返し終了します。ユーザーは確認の質問の後、認証情報をキーリングに保存するかどうかを選択できます。 -
VCS リポジトリは最初の方法(1)を適用し、インデックスサーバーは両方の方法を適用します。
隔離された環境でスクリプトを実行する
PDM を使えば、ローカルパッケージを読み込んだ状態で、任意のスクリプトやコマンドを実行できます。
$ pdm run flask run -p 54321
また、pyproject.toml
中の [tool.pdm.scripts]
セクションでカスタムスクリプトショートカットをサポートしています。
pdm run <shortcut_name>
でスクリプトを呼び出すことができます。例えば、
[tool.pdm.scripts]
start_server = "flask run -p 54321"
としておき、ターミナルで
[tool.pdm.scripts]
start_server = "flask run -p 54321"
コマンドに引数を追加することもできます。
$ pdm run start_server -h 0.0.0.0
Flask server started at http://0.0.0.0:54321
PDM は 3 タイプのスクリプトをサポートしています。
ノーマルコマンド
プレーンテキストスクリプトは通常のコマンドとしてみなされますが、明示的に指定することもできます。
[tool.pdm.scripts]
start_server = {cmd = "flask run -p 54321"}
パラメータの間にコメントを入れたい場合などは、文字列の代わりに配列を使ってコマンドを指定すると便利です。
[tool.pdm.scripts]
start_server = {cmd = [
"flask",
"run",
# Important comment here about always using port 54321
"-p", "54321"
]}
シェルスクリプト
パイプラインやリダイレクトといった、シェル特有のタスクを実行したい時にはシェルスクリプトを利用すると便利です。基本的に、shell=True
とした subprocess.Popen()
を介して実行されます。
[tool.pdm.scripts]
filter_error = {shell = "cat error.log|grep CRITICAL > critical.log"}
Python の関数を呼び出す
<module_name>:<func_name>
という形式でPython の関数を呼ぶようにスクリプトを定義できます。
[tool.pdm.scripts]
foobar = {call = "foo_package.bar_module:main"}
関数にはリテラルの引数を与えることができます。
[tool.pdm.scripts]
foobar = {call = "foo_package.bar_module:main('dev')"}
環境変数のサポート
実行中のシェルでセットされた環境変数は全て pdm run
で見ることができ、実行時に展開されます。また、 pyproject.toml
で固定の環境変数を定義できます。
[tool.pdm.scripts]
start_server.cmd = "flask run -p 54321"
start_server.env = {FOO = "bar", FLASK_ENV = "development"}
TOML's syntax を使って複合辞書を定義していることに注意してください。
env_file = "<file_path>
"` の設定で dotenv ファイルもサポートされています。
全てのスクリプトで共有される環境変数や dotenv ファイルについては、tool.pdm.scripts
テーブルの _
という特別なキーに続く env
や env_file
で設定できます。
[tool.pdm.scripts]
_.env_file = ".env"
start_server = "flask run -p 54321"
migrate_db = "flask db upgrade"
スクリプトショートカット一覧を表示する
pdm run --list/-l
で利用可能なスクリプトショートカットの一覧を表示できます。
$ pdm run --list
Name Type Script Description
----------- ----- ---------------- ----------------------
test_cmd cmd flask db upgrade
test_script call test_script:main call a python function
test_shell shell echo $FOO shell command
キャッシュの管理
キャッシュ管理のための便利なコマンドグループを提供します。4 種類のキャッシュが存在しています。
-
wheels/
: non-wheel な配布物、ファイルのビルド結果を格納します -
http/
: HTTP のレスポンスコンテンツを格納します -
metadata/
: リゾルバが取得したパッケージメタデータを格納します -
hashes/
: パッケージのインデックスから取得したハッシュやローカルで計算したハッシュを格納します
現在のキャッシュの使用状況を確認するには pdm cache info
を使ってください。また、キャッシュコンテンツの管理のため、add
, remove
, list
といったサブコマンドも利用できます。使い方はそれぞれのコマンドで --help
オプションを指定すれば確認できます。
どうやって Python インタープリタに PEP 582 パッケージを利用可能にさせたか
Python のスタートアップ時に site-packages
がロードされるようになったおかげです。PDM に同梱されている sitecustomize.py
を実行することで、 sys.path
にパッチを当てることができます。インタープリタはディレクトリを探索して最も近傍の __pypackage__
フォルダを探し、それを sys.path
に追加します。
プロジェクトファイルのシンタックス
プロジェクトメタデータ
PDM は PEP 621 の標準形式に基づいてプロジェクトメタデータを読み込みます。詳細は PEP をみてください。
このドキュメントの以下の部分では、[project]
テーブルが明示的に与えられていない場合、[project]
テーブルの下に書かれていると考えててください
パッケージのバージョンを動的に判断する
version = {from = "pdm/__init__.py"}
のように、 フィールドにファイルソースを指定できます。この形式では、バージョンはそのファイル中の __version__
変数の値が読み込まれます。
PDM は SCM[6] タグからバージョンを読み込むことができます。git や hg といったバージョンコントロールシステムを使っているなら、[version]
にこんな感じに定義してください。
version = {use_scm = true}
いずれの場合でも、dynamic
フィールドに version
を含めなければなりません。そうでなければバックエンドがエラーになります。
dynamic = ["version"]
パッケージファイルのインクルード、エクスクルード
ファイルのインクルード、エクスクルードはグロブパターンを使ってシンプルに指定できます。
includes = [
"**/*.json",
"mypackage/",
]
excludes = [
"mypackage/_temp/*"
]
includes
と excludes
のどちらも与えられていない場合、PDM はトップレベルパッケージとその中の全てのデータファイルをインクルードします。PDM が見つけられるように、パッケージを src
ディレクトリに配置することもできます。
パッケージ検索のためのパッケージディレクトリ選択
setuptools
の package_dir
設定のように、別のパッケージディレクトリを指定することができます。pyproject.toml
に src
のように簡単にディレクトリを指定できます。
package-dir = "src"
パッケージディレクトリが与えられない場合、以下の条件で暗黙に src
を package-dir
として認識します。
-
src/__init__.py
が存在しない場合、つまり有効な Python パッケージでないという意味 -
src/*
の下に幾つかのパッケージが存在する
暗黙のネームスペースパッケージ
PEP 420 に指定されている通り、以下の場合にディレクトリはネームスペースパッケージとして認識されます。
-
<package>/__init__.py
が存在せず、 -
<package>/*
の下に、通常のパッケージや他のネームスペースパッケージが存在する場合 -
<package>
はpackage-dir
として指定されない
依存関係指定
project.dependencies
はPEP 404 と PEP 508 に従う、依存関係指定文字列配列です。
例えば
dependencies = [
# Named requirement
"requests",
# Named requirement with version specifier
"flask >= 1.1.0",
# Requirement with environment marker
"pywin32; sys_platform == 'win32'",
# URL requirement
"pip @ https://github.com/pypa/pip.git@20.3.1"
]
editable な要件
通常の依存関係指定の他に、エディタブルモードでパッケージをインストールすることができます。エディタブル指定形式は、Pip's editable install mode と同じです。
例えば
dependencies = [
...,
"-e path/to/SomeProject",
"-e git+http://repo/my_project.git#egg=SomeProject"
]
任意の依存関係
setuptools
の extras_require
パラメータと同様、幾つかの要件をオプションにすることができます。
[project.optional-dependencies]
socks = [ 'PySocks >= 1.5.6, != 1.5.7, < 2' ]
tests = [
'ddt >= 1.2.2, < 2',
'pytest < 6',
'mock >= 1.0.1, < 4; python_version < "3.4"',
]
任意の依存関係グループをインストールするには、
$ pdm install -s socks
-s
オプションを複数回し丁すると、1 グループ以上含めることができます。
開発のための依存関係
package.json
の dev-dependencies
と同様、開発のための依存関係も指定可能です。
[project]
dev-dependencies = [
"pytest",
"flake8",
"black"
]
インストールするには、
$ pdm install -d
コンソールスクリプト
[project.scripts]
mycli = "mycli.__main__:main"
この指定はsetuptools
スタイルに翻訳されます
entry_points = {
'console_scripts': [
'mycli=mycli.__main__:main'
]
}
また、[project.gui-scripts]
は setuptools
スタイルの gui_scripts
エントリーポイントグループに翻訳されます。
エントリーポイント
エントリーポイントのスタイルは、[project.entry-points.<type>]
セクションで [project.scripts]
と同じ形式で指定できます。
[project.entry-points.pytest11]
myplugin = "mypackage.plugin:pytest_plugin"
C 拡張をビルドする
現在でも、C 拡張のビルドは setuptools
に依存しています。setup()
のパラメータの辞書を唯一の引数として受け取る build
という関数を含む python スクリプトを作成してください。関数内の ext_module
設定で辞書を更新します。
MarkupSafe
から引用した例はこちら。
# build.py
from setuptools import Extension
ext_modules = [Extension("markupsafe._speedups", ["src/markupsafe/_speedups.c"])]
def build(setup_kwargs):
setup_kwargs.update(ext_modules=ext_modules)
そして、project.toml
の build
にビルドスクリプトパスを指定してください。
# pyproject.toml
[project]
build = "build.py"
発展的な使い方
Tox による自動テスト
Tox は複数の Python バージョンや依存関係のセットに対してテストを行う偉大なツールです。以下のように、tox.ini
中でPDM を使ったテストを埋め込むことができます。
[tox]
env_list = py{36,37,38},lint
isolated_build = true
passenv = PDM_IGNORE_SAVED_PYTHON=1
[testenv]
deps = pdm
commands =
pdm install --dev
pytest tests
[testenv:lint]
deps = pdm
commands =
pdm install -s lint
flake8 src/
Tox によって作られた virtualenv を使う場合、pdm config use_venv ture
としていなければいけません。PDM は pdm.lock
から virtualenv に 依存関係をインストールします。専用の venv 環境であれば、pdm run pytest tests/
の代わりに pytest tests/
で直接ツールを実行できます。
テストコマンドにおいて、pdm add/pdm remove/pdm update/pdm lock
を実行しないように。そうでないと、pdm.lock
ファイルが予期せず変更されてしまいます。
追加の依存関係は deps
設定で行えます。また、PDM を正常に動作させるために、上記で示したように、isolated_build
と passenv
の設定を行ってください。
このような制約を容易に解消するたに、Tox プラグインである tox-pdm が用意されています。インストールは
$ pip install tox-pdm
または
$ pdm add --dev tox-pdm
すると、以下のように tox.ini
をより簡潔に記述できます。
[tox]
env_list = py{36,37,38},lint
[testenv]
sections = dev
commands =
pytest tests
[testenv:lint]
sections = lint
commands =
flake8 src/
詳細なガイダンスはproject's README を参照してください。
Nox を使った自動テスト
Nox もまた、自動テストのための偉大なツールです。tox と異なり、Nox は設定に標準の Python ファイルを用います。
Nox で PDM を使用するのはとても簡単で、以下に noxfile.py
を示します。
import os
import nox
os.environ.update({"PDM_IGNORE_SAVED_PYTHON": "1"})
@nox.session
def tests(session):
session.run('pdm', 'install', '-s', 'test', external=True)
session.run('pytest')
@nox.session
def lint(session):
session.run('pdm', 'install', '-s', 'lint', external=True)
session.run('flake8', '--import-order-style', 'google')
PDM が virtualenv の Python を正しく選択できるように、PDM_IGNORE_SAVED_PYTHON
を設定してください。また、pdm
が PATH
に含まれていることを確認してくだい。nox を実行する前に、venv の再利用の有効化のため pdm confing use_venv
を true
に設定してください。
継続的インテグレーションにおける PDM の使用
PDM は Python < 3.7 ではインストールできないので、もしプロジェクトを Python 3.7 以下でテストする場合、PDM が正しい Python のバージョンでインストールされているか確認する必要があります。特定のジョブやタスクが実行されるターゲットの Python のバージョンとは異なります。
もし、GitHub Action を使用しているならば、pdm-project/setup-pdm がこのプロセスを簡単に実行できます。GitHub Actions のワークフローを以下に示しますが、他の CI プラットフォームにも適用可能です。
Testing:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
os: [ubuntu-latest, macOS-latest, windows-latest]
steps:
- uses: actions/checkout@v1
- name: Set up PDM
uses: pdm-project/setup-pdm@v1.1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pdm sync -d -s testing
- name: Run Tests
run: |
pdm run -v pytest tests
設定
利用可能な設定
設定 | 説明 | デフォルト | プロジェクトで利用可能か | 環境変数 |
---|---|---|---|---|
cache_dir |
キャッシュファイルのルートディレクトリ | OS のデフォルト位置 | No | |
auto_global |
ローカルプロジェクトが存在しない場合、暗黙にグローバルパッケージを使用するか | False |
No | PDM_AUTO_GLOBAL |
use_venv |
PEP 582 の代わりに、アクティベートされた venv 環境の site-packages にパッケージをインストールする | False |
Yes | PDM_USE_VENV |
parallel_install |
インストールやアンインストールをパラレルに実行するかどうか | True |
Yes | PDM_PARALLEL_INSTALL |
python.path |
Python インタープリタパス | Yes | PDM_PYTHON_PATH |
|
python.use_pyenv |
pyenv インタープリタを使う | True |
Yes | |
pypi.url |
PyPI ミラーの URL |
pip.conf の index-url を読み込むか、見つからなければ https://pypi.org/simple を使用する |
Yes | PDM_PYPI_URL |
pypi.verify_ssl |
PyPI への問い合わせ時に SSL 証明書を確認する |
pip.conf の trusted-hosts を読み込む。デフォルトは True
|
Yes | |
pypi.json_api |
パッケージメタデータについては PyPI の JSON API を参照してください | False |
Yes | PDM_PYPI_JSON_API |
strategy.save |
パッケージが追加された際に、バージョンの保存方法を指定 |
compatible (または exact , wildcard ) |
Yes | |
strategy.update |
パッケージのアップデートのデフォルトの挙動 |
reuse (または eager ) |
Yes | |
strategy.resolve_max_rounds |
解決プロセスの最大ラウンド数 | 1000 | Yes | PDM_RESOLVE_MAX_ROUNDS |
対応する環境変数が設定されている場合、その値は設定ファイルに保存されているものよりも優先されます。
プラグインを書く
PDM はコミュニティードリブンなパッケージマネージャを目指しています。フル機能のプラグインシステムを搭載しており、以下のようなことが可能です。
- PDM の新しいコマンドを開発する
- 既存の PDM のコマンドに新しいオプションを追加する
- 追加の設定項目の読み込みによって、PDM の挙動を変更する
- 依存関係解決やインストールプロセスをコントロールする
プラグインは何をすべきか
PDM プロジェクトは依存関係の管理とパッケージの公開にフォーカスしています。PDM に統合したい他の機能については独自のプラグインを作成し、スタンドアロンな PyPI プロジェクトとしてリリースすることが望ましいです。プラグインがコアプロジェクトにとって有益であると判断された場合、PDM 本体に吸収される可能性があります。
独自のプラグインを書く
以下のセクションでは、hello.name
という設定を読み込む hello
コマンドを追加する例を紹介します。
コマンドを書く
PDM の CLI モジュールは、ユーザーが簡単に「継承と修正」ができるように設計されています。新しいコマンドはこんな感じに書きます。
from pdm.cli.commands.base import BaseCommand
class HelloCommand(BaseCommand):
"""Say hello to the specified person.
If none is given, will read from "hello.name" config.
"""
def add_arguments(self, parser):
parser.add_argument("-n", "--name", help="the person's name to whom you greet")
def handle(self, project, options):
if not options.name:
name = project.config["hello.name"]
else:
name = options.name
print(f"Hello, {name}")
まずは、pdm.cli.command.base.BaseCommand
を継承した HolloCommand
クラスを作りましょう。クラスには 2 つの主要な関数を実装します。
- コマンドライン引数を追加するための、引数パーサーを唯一の引数として受け取る
add_arguments()
- サブコマンドがマッチした時に何かしらの動作をする
handle()
、pass
を書くと何もしません。handle
は 2 つの引数を受け取ります。pdm.project.Project
オブジェクトと、 パースされたargmarse.Namespace
オブジェクトです。
ドキュメントストリングはコマンドヘルプテキストとして使用され、 pdm --help
で表示されます。
また、PDM のサブコマンドには 2 つのデフォルトオプションがあります。-v/--verbose
冗長表示レベルと-g/--global
グローバルプロジェクトを有効化するオプションです。これらのオプションが必要でない場合、 クラスアトリビュートの arguments
を pdm.cli.options.Option
オブジェクトのリストで上書きするか、デフォルトオプションを持たないように空のリストを設定してください。
class HelloCommand(BaseCommand):
arguments = []
コアオブジェクトにコマンドを登録する
作成したプラグインプロジェクトのどこかに PDM コアオブジェクトをただ一つの引数として受け取る関数を書いてください。ファンクション名はどんなものでも構いません。
def hello_plugin(core):
core.register_command(HelloCommand, "hello")
コマンドを登録するために、core.register_comman()
を呼び出してください。第二引数はサブコマンドの名前で、任意です。サブコマンドの名前が渡されない場合、PDM は HelloCommand
の name
アトリビュートを使用します。
新しい設定項目を追加する
最初のコードスニペットを思い出してください。hello.name
コンフィグキーはコマンドラインから渡されない場合に参照されます。
class HelloCommand(BaseCommand):
"""Say hello to the specified person.
If none is given, will read from "hello.name" config.
"""
def add_arguments(self, parser):
parser.add_argument("-n", "--name", help="the person's name to whom you greet")
def handle(self, project, options):
if not options.name:
name = project.config["hello.name"]
else:
name = options.name
print(f"Hello, {name}")
これまでは、pdm config get hello.name
で設定値を確認しようとしても、有効なコンフィグキーではない、とエラーが発生していました。コンフィグ項目の登録も必要です。
from pdm.project.config import ConfigItem
def hello_plugin(core):
core.register_command(HelloCommand, "hello")
core.add_config("hello.name", ConfigItem("The person's name", "John"))
ConfigItem
クラスは 4 つのパラメータを受け取ります。順番に、
-
description
: 設定項目の説明 -
default
: 設定項目のデフォルト値 -
global_only
: 設定がグローバル設定でのみ有効化どうか -
env_var
: 設定値として読み込まれる環境変数の名前
その他のプラグインのポイント
コマンドや設定の他にも、PDM には上記の例ではカバーしきれないいくつかの機能があります。
-
core.project_class
: プロジェクトオブジェクトのクラスを変更します。 -
core.repository_class
: リポジトリオブジェクトのクラスを変更し、パッケージの候補やパッケージメタデータをどのように探すかを制御します。 -
core.resolver_class
: 解決プロセスを制御するために、リゾルバクラスを変更します。 -
core.synchronizer_class
: インストールプロセスを制御するために、synchronizer_class を変更します。 -
core.parser
: 大元の 引数パーサーに引数を追加します。
PDM プラグイン開発のための Tips
プラグイン開発の際には、開発中のものをアクティベートしてプラグインして、コードが変更されたらアップデートされると嬉しいです。pip install -e .
で通常実現可能です。或いは python setup.py develop
でも可能です。伝統的な Python パッケージングの世界では setup.py
がこの役目を負っています。しかし、PDM プロジェクトでは setup.py
なんてありません。どのようにすれば良いでしょうか?
幸運なことに、PDM と PEP 582 により簡単に実現できます。まず、ドキュメントに従って PEP 582 をグローバルで有効化します。そして __pypackage__
ディレクトリに全ての依存関係をインストールするだけです。
$ pdm install -d
すると、全ての依存関係は、互換性のある Python インタープリタとともに、プラグインが含まれた状態で、エディタブルモードで利用可能になります。これは、再インストールすることなしに、コード変更が直ちに影響を及ぼすことを意味します。pdm
実行ファイルは Python インタープリタを使用しています。プラグインプロジェクト下で pdm
を実行すると、開発中のプラグインは自動的に有効化されどのように動くかテストできます。これは、開発ワークフローに PEP 582 がどのように利益をもたらしているかを示すものです。
プラグインを公開する
プラグインが完成したら、PyPI で配布しましょう。 PDM のプラグインはエントリーポイントタイプによって発見されます。pdm.plugin
エントリーポイントを作成し、プラグインが呼び出し可能になるようにしましょう (関数である必要はなく、callable オブジェクトであればなんでも構いません)。
PEP 621
# pyproject.toml
[project.entry-points.pdm]
hello = "my_plugin:hello_plugin"
setuptools
# setup.py
setup(
...
entry_points={"pdm": ["hello = my_plugin:hello_plugin"]}
...
)
プラグインのアクティベート
エントリーポイントからプラグインがロードされるため、プラグインをインストールするだけで有効化されます。
pdm-hello
としてプラグインが公開されたら、例えば pipx
経由でpdm
をインストールをした場合には
$ pipx inject pdm pdm-hello
或いは pdm
を homebrew
でインストールした場合は
$ $(brew --prefix pdm)/libexec/bin/python -m pip install pdm-hello
または、pip install
で pdm
をインストールした場合は
$ pip install --user pdm-hello
中心となる考え方は、pdm
と同じ site-packages ディレクトリにプラグインをインストールする、ということです。
ターミナルで pdm --help
を実行してみましょう。hello
コマンドが追加されていることが確認でき、以下のように実行可能です。
$ pdm hello Jack
Hello, Jack
PDM にコントリビュートする
まず最初に、コントリビュートしてくれることに感謝します。コントリビュートには以下のものが含まれますが、これに制限されるものではありません。
- バグの報告
- コードへのコントリビュート
- テストの作成
- ドキュメントの作成
以下は、コントリビュートについてのガイドラインです。
オープンソースプロジェクトへコントリビュートする際に推奨されるフロー
このガイドラインは OSS ビギナーへのガイドラインになっています。もし、あなたが OSS 開発者の経験があるなら、このセクションはスキップできます。
-
最初に、リポジトリページの右上のフォークボタンを押して、自身のネームスペースへプロジェクトをフォークしてください。
-
ローカルに、上流の リポジトリをクローンしてください。
$ git clone https://github.com/pdm-project/pdm.git # Or if you prefer SSH clone: $ git clone git@github.com:pdm-project/pdm.git
-
フォークを新しいリモートに設定してください。
$ git remote add fork https://github.com/yourname/pdm.git $ git fetch fork
ここで、
fork
とあるのは、フォークリポジトリのリモートの名前です。
ProTips
-
マスターブランチのコードを変更しないでください。マスターブランチは、常に origin/master を追跡する必要があります。
マスターブランチを最新にアップデートするには、
$ git pull origin master # In rare cases that your local master branch diverges from the remote master: $ git fetch origin && git reset --hard master
-
アップデートしたマスターブランチを元に、パッチ作成のための新しいブランチを作成します。
-
パッチのブランチからプルリクエストを作成します。
ローカルでの開発
テストスイートを正しく実行するには、Git LFS をインストールする必要があります。
$ git lfs install
次に、venv 環境に基本的な依存関係をインストールする必要があります。PDM は依存関係のインストールにローカルパッケージディレクトリを使うにも関わらず、最初の PDM のスタートアップには venv 環境が必要です。
$ python setup_dev.py
全ての依存関係が、ローカルの __pypackages__
ディレクトリにインストールされたので、この時点で開発環境として使用できます。__pypackage__/<VERSION>/bin
に存在する pdm
の実行ファイルは、エディタブルモードでインストールされている外側から直接実行することもできますし、venv 環境の中から python -m pdm
として実行することもできます。
テストの実行
$ pdm run test
テストスイートはまだシンプルで追加する必要があるので、テストケースの作成にご協力ください。
コードスタイル
PDM はリントに pre-commit
を使用するので、まずはこれをインストールします。
$ pre-commit install
$ pdm run lint
PDM はblack
コーディングスタイルとインポート宣言に isort
を使用します。もしこれに従わなければ CI は失敗し、プルリクエストはマージされません。
ドキュメントのプレビュー
もし docs/
を変更をし、ビルド結果を確認したいなら、以下を実行してください。
$ pdm run doc
Discussion