Skip to content

PDM Scripts#

npm runと同様に、PDMではローカルパッケージをロードして任意のスクリプトやコマンドを実行できます。

Arbitrary Scripts#

1
pdm run flask run -p 54321

プロジェクト環境内のパッケージを認識する環境でflask run-p 54321を実行します。

Single-file Scripts#

Added in version 2.16.0

PDMでは、インラインスクリプトメタデータを使用して単一ファイルスクリプトを実行できます。

メタデータが埋め込まれたスクリプトの例を次に示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# test_script.py
# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "requests<3",
#   "rich",
# ]
# ///

import requests
from rich.pretty import pprint

resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])

pdm run test_script.pyで実行すると、PDMは指定された依存関係がインストールされた一時的な環境を作成し、スクリプトを実行します。:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[
│   ('1', 'PEP Purpose and Guidelines'),
│   ('2', 'Procedure for Adding New Modules'),
│   ('3', 'Guidelines for Handling Bug Reports'),
│   ('4', 'Deprecation of Standard Modules'),
│   ('5', 'Guidelines for Language Evolution'),
│   ('6', 'Bug Fix Releases'),
│   ('7', 'Style Guide for C Code'),
│   ('8', 'Style Guide for Python Code'),
│   ('9', 'Sample Plaintext PEP Template'),
│   ('10', 'Voting Guidelines')
]

前回作成した環境を再利用する場合は、--reuse-envオプションを追加します。また、スクリプトメタデータに[tool.pdm]セクションを追加して、PDMを設定することもできます。次に例を示します。:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# test_script.py
# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "requests<3",
#   "rich",
# ]
#
# [[tool.pdm.source]]  # Use a custom index
# url = "https://mypypi.org/simple"
# name = "pypi"
# ///

詳細については、specificationを参照してください。

User Scripts#

PDMは、pyproject.tomlのオプションの[tool.pdm.scripts]セクションにあるカスタムスクリプトショートカットもサポートしています。

Note

[tool.pdm.scripts]ディレクトリを[project.scripts]と混同しないでください。後者は、ここで説明されているように、パッケージの一部としてコマンドをインストールするために使用されます。

その後、pdm run<script_name>を実行して、PDMプロジェクトのコンテキストでスクリプトを呼び出すことができます。次に例を示します。

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

次にターミナルで:

1
2
$ pdm run start
Flask server started at http://127.0.0.1:54321

次の引数がコマンドに追加されます:

1
2
$ pdm run start -h 0.0.0.0
Flask server started at http://0.0.0.0:54321

Yarn-like script shortcuts

組み込みのショートカットがあり、スクリプトが組み込みコマンドまたはプラグインが提供するコマンドと競合しない限り、すべてのスクリプトをルートコマンドとして使用できます。つまり、startスクリプトがあれば、pdm run startとpdm startの両方を実行できます。しかし、installスクリプトがあれば、pdm run installだけがそれを実行し、pdm installは組み込みのinstallコマンドを実行します。

PDMでは、次の4種類のスクリプトがサポートされています。:

cmd#

プレーンテキストスクリプトは通常のコマンドと見なされますが、次のように明示的に指定することもできます。::

1
2
[tool.pdm.scripts]
start = {cmd = "flask run -p 54321"}

パラメータ間にコメントを追加する場合など、より便利な場合もあります。コマンドを文字列ではなく配列として指定するには、次のようにします。:

1
2
3
4
5
6
7
[tool.pdm.scripts]
start = {cmd = [
    "flask",
    "run",
    # Important comment here about always using port 54321
    "-p", "54321"
]}

shell#

シェルスクリプトを使用すると、パイプラインや出力のリダイレクトなど、シェル固有のタスクを実行できます。これは基本的にsubprocess.Popen()とshell=Trueを使用して実行されます:

1
2
[tool.pdm.scripts]
filter_error = {shell = "cat error.log|grep CRITICAL > critical.log"}

call#

スクリプトは、<module_name>:<func_name>の形式でpython関数を呼び出すように定義することもできます。:

1
2
[tool.pdm.scripts]
foobar = {call = "foo_package.bar_module:main"}

この関数には、リテラル引数を指定できます。:

1
2
[tool.pdm.scripts]
foobar = {call = "foo_package.bar_module:main('dev')"}

composite#

このスクリプトは、他の定義済みスクリプトを実行します。:

1
2
3
4
[tool.pdm.scripts]
lint = "flake8"
test = "pytest"
all = {composite = ["lint", "test"]}

pdm run allを実行すると、まずlintが実行され、lintが成功した場合はtestが実行されます。

Added in version 2.13.0

デフォルトの動作を上書きし、失敗後に残りのスクリプトの実行を継続するには、keep_goingオプションをtrueに設定します。

1
2
3
4
5
[tool.pdm.scripts]
lint = "flake8"
test = "pytest"
all.composite = ["lint", "test"]
all.keep_going = true

keep_goingがtrueに設定されている場合、複合スクリプトの戻りコードは、すべてが成功した場合は'0'、または最後に失敗した個々のスクリプトのコードのいずれかになります。

呼び出されたスクリプトに引数を指定することもできます。

1
2
3
4
[tool.pdm.scripts]
lint = "flake8"
test = "pytest"
all = {composite = ["lint mypackage/", "test -v tests/"]}

Note

コマンド行で渡された引数は、呼び出された各タスクに与えられます。

compositeスクリプトを使用して、複数のコマンドを組み合わせることもできます。:

1
2
3
4
5
[tool.pdm.scripts]
mytask.composite = [
    "echo 'Hello'",
    "echo 'World'"
]

Script Options#

env#

現在のシェルに設定されているすべての環境変数はpdm runで見ることができ、実行時に展開されます。さらに、pyproject.tomlでいくつかの固定環境変数を定義することもできます。

1
2
3
[tool.pdm.scripts]
start.cmd = "flask run -p 54321"
start.env = {FOO = "bar", FLASK_DEBUG = "1"}

Note how we use TOML's syntax to define a composite dictionary.

複合辞書を定義するためにTOML's syntaxをどのように使用しているかに注目してください。

About environment variable substitution

"環境変数の代入について"スクリプト仕様の変数は、すべてのスクリプトタイプで代入できます。cmdスクリプトでは、${VAR}構文のみがすべてのプラットフォームでサポートされていますが、shellスクリプトでは、構文はプラットフォームに依存します。たとえば、Windows cmdは%VAR%を使用し、bashは$VARを使用します。

Note

複合タスクレベルで指定された環境変数は、呼び出されたタスクで定義された環境変数よりも優先されます。

env_file#

すべての環境変数をdotenvファイルに保存して、PDMに読み取らせることもできます。:

1
2
3
[tool.pdm.scripts]
start.cmd = "flask run -p 54321"
start.env_file = ".env"

The variables within the dotenv file will not override any existing environment variables. If you want the dotenv file to override existing environment variables use the following:

dotenvファイル内の変数は、既存の環境変数を上書きしません。 dotenvファイルで既存の環境変数を上書きする場合は、次のようにします。:

1
2
3
[tool.pdm.scripts]
start.cmd = "flask run -p 54321"
start.env_file.override = ".env"

Environment variable loading order

異なるソースからロードされた環境変数は、次の順序でロードされます。:

  1. OS環境変数
  2. PDM_PROJECT_ROOT、PATH、VIRTUAL_ENVなどのプロジェクト環境
  3. env_fileで指定されたDotenvファイル
  4. envで指定された環境変数のマッピング

後者のソースからの環境変数は、前者のソースからの環境変数を上書きします。複合タスクレベルで指定されたdotenvファイルは、呼び出されたタスクによって定義された環境変数を上書きします。

env varには、以前にロードされたソースからの別のenv varへの参照を含めることができます。次に例を示します。:

1
2
VAR=42
FOO=hello-${VAR}
結果はFOO=hello-42になります。この参照には、${VAR:-default}という構文でデフォルト値を含めることもできます。

working_dir#

Added in version 2.13.0

スクリプトの現在の作業ディレクトリを設定できます。

1
2
3
[tool.pdm.scripts]
start.cmd = "flask run -p 54321"
start.working_dir = "subdir"

相対パスは、プロジェクトのルートに対して解決されます。

site_packages#

実行環境が外部のPythonインタプリタから適切に分離されていることを確認するために、次のいずれかの条件が満たされない限り、選択されたインタプリタのsite-packagesはsys.pathにロードされません。

  1. 実行ファイルはPATHからのものですが、__pypackages__フォルダ内にはありません。
  2. pdm runの後に-s/--site-packagesフラグがあります。
  3. site_packages=trueは、スクリプトテーブルまたはグローバル設定キー_のいずれかにあります。

PEP 582を有効にして(pdm run接頭辞なしで)実行している場合、site-packagesは常にロードされることに注意してください。

Shared Options#

pdm runで実行されるすべてのタスクでオプションを共有する場合は、[tool.pdm.scripts]テーブルの特別なキー_の下にオプションを記述します。:

1
2
3
4
[tool.pdm.scripts]
_.env_file = ".env"
start = "flask run -p 54321"
migrate_db = "flask db upgrade"

また、タスク内では、環境変数PDM_PROJECT_ROOTがプロジェクトルートに設定されます。

Arguments placeholder#

デフォルトでは、ユーザが指定した追加引数はすべて、単にコマンド(またはcompositeタスクのすべてのコマンド)に追加されます。

ユーザが提供する追加引数をより詳細に制御したい場合は、{args}プレースホルダを使用できます。これはすべてのスクリプトタイプで使用でき、それぞれに対して適切に補間されます。

1
2
3
4
[tool.pdm.scripts]
cmd = "echo '--before {args} --after'"
shell = {shell = "echo '--before {args} --after'"}
composite = {composite = ["cmd --something", "shell {args}"]}

これは以下の補間を生成すします(これらは実際のスクリプトではなく、ここでは補間を説明するためだけのものです)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$ pdm run cmd --user --provided
--before --user --provided --after
$ pdm run cmd
--before --after
$ pdm run shell --user --provided
--before --user --provided --after
$ pdm run shell
--before --after
$ pdm run composite --user --provided
cmd --something
shell --before --user --provided --after
$ pdm run composite
cmd --something
shell --before --after

ユーザー引数を指定しない場合に使用されるデフォルト値をオプションで指定できます。

1
2
[tool.pdm.scripts]
test = "echo '--before {args:--default --value} --after'"

will produce the following:

1
2
3
4
$ pdm run test --user --provided
--before --user --provided --after
$ pdm run test
--before --default --value --after

Note

プレースホルダが検出されるとすぐに、引数は追加されなくなります。これはcompositeスクリプトにとって重要です。なぜなら、サブタスクの1つでプレースホルダが検出されても、サブタスクの引数が追加されないため、プレースホルダを必要とするすべてのネストされたコマンドに明示的にプレースホルダを渡す必要があるからです。

Note

callスクリプトは{args}プレースホルダをサポートしていません。なぜなら、そのような複雑なケースなどを処理するためにsys.argvに直接アクセスできるからです。

{pdm} placeholder#

複数のPDMがインストールされている場合や、pdmが別の名前でインストールされている場合があります。これは、CI/CDの状況や、異なるリポジトリで異なるPDMバージョンを使用している場合などに発生します。スクリプトをより堅牢にするには、{pdm}を使用してスクリプトを実行するPDMエントリポイントを使用します。これは{sys.executable} -m pdmに展開されます。

1
2
[tool.pdm.scripts]
whoami = { shell = "echo `{pdm} -V` was called as '{pdm} -V'" }

次のように出力されます。:

1
2
3
4
5
$ pdm whoami
PDM, version 0.1.dev2501+g73651b7.d20231115 was called as /usr/bin/python3 -m pdm -V

$ pdm2.8 whoami
PDM, version 2.8.0 was called as <snip>/venvs/pdm2-8/bin/python -m pdm -V

Note

上記の例ではPDM 2.8を使用していますが、この機能は2.10シリーズで導入され、Showcase用にバックポートされただけです。

Show the List of Scripts#

使用可能なスクリプトショートカットのリストを表示するには、pdm run --list/-lを使用します。:

1
2
3
4
5
6
7
8
$ pdm run --list
╭─────────────┬───────┬───────────────────────────╮
│ Name        │ Type  │ Description               │
├─────────────┼───────┼───────────────────────────┤
│ test_cmd    │ cmd   │ flask db upgrade          │
│ test_script │ call  │ call a python function    │
│ test_shell  │ shell │ shell command             │
╰─────────────┴───────┴───────────────────────────╯

スクリプトの説明にhelpオプションを追加すると、上記の出力のDescription列に表示されます。

Note

名前がアンダースコア(_)で始まるタスクは内部タスク(helpers...)と見なされ、リストには表示されません。

Pre & Post Scripts#

npmと同様に、PDMはプリスクリプトとポストスクリプトによるタスクの構成もサポートしており、プリスクリプトは指定されたタスクの前に実行され、ポストスクリプトは後に実行されます。

1
2
3
4
[tool.pdm.scripts]
pre_compress = "{{ Run BEFORE the `compress` script }}"
compress = "tar czvf compressed.tar.gz data/"
post_compress = "{{ Run AFTER the `compress` script }}"

この例では、pdm run compressはこれら3つのスクリプトをすべて順番に実行します。

The pipeline fails fast

pre - self - postスクリプトのパイプラインでは、失敗すると後続の実行がキャンセルされます。

Hook Scripts#

特定の状況では、PDMは実行する特殊なフックスクリプトを検索します。

  • post_init:pdm initの後に実行します
  • pre_install:パッケージをインストールする前に実行します
  • post_install:パッケージがインストールされた後に実行されます。
  • pre_lock:依存性解決の前に実行
  • post_lock:依存関係の解決後に実行
  • pre_build:配布物を構築する前に実行します
  • post_build:ディストリビューションがビルドされた後に実行されます。
  • pre_publish:配布物を公開する前に実行します
  • post_publish:配布物が公開された後に実行されます。
  • pre_script:スクリプトの前に実行されます。
  • post_script:任意のスクリプトの後に実行します。
  • pre_run:runスクリプトを呼び出す前に一度実行します。
  • post_run:runスクリプトを呼び出した後に一度実行します。

Note

事前スクリプトと事後スクリプトは引数を受け取ることができません。

Avoid name conflicts

[tool.pdm.scripts]テーブルの下にinstallスクリプトが存在する場合、pre_installスクリプトはpdm installとpdm run installの両方によってトリガされます。そのため、保存された名前は使用しないことをお勧めします。

Note

複合タスクには、事前スクリプトと事後スクリプトを含めることもできます。呼び出されたタスクは、独自の事前スクリプトと事後スクリプトを実行します。

Skipping scripts#

フックや事前スクリプトと事後スクリプトなしでスクリプトを実行することが望ましい場合があるため、preとpostのすべてのフックを無効にする--skip=:allがあります。また、--skip=:preと--skip=:postでは、それぞれすべてのpre_*フックとすべてのpost_*フックをスキップすることができます。

前のスクリプトは必要だが後のスクリプトは必要ない場合や、複合タスクから1つを除くすべてのタスクが必要な場合もあります。これらのユースケースでは、除外するタスクまたはフック名のリストを受け入れる細粒状--skipパラメータがあります。

1
pdm run --skip pre_task1,task2 my-composite

このコマンドはmy-compositeタスクを実行し、pre_task1フックとtask2およびそのフックをスキップします。

PDM_SKIP_HOOKS環境変数にスキップリストを指定することもできますが、--skipパラメータを指定するとすぐに上書きされます。

フックとプリ/ポストスクリプトの動作の詳細については、the dedicated hooks pageを参照してください。