PDM 脚本#
与 npm run
类似,使用 PDM,你可以在加载本地包的情况下运行任意脚本或命令。
任意脚本#
1 |
|
它将在知晓项目环境中的包的环境中运行 flask run -p 54321
。
单文件脚本#
在版本 2.16.0 中添加
PDM 能够运行带有 内联脚本元数据 的单文件脚本。
以下是一个带有嵌入式元数据的脚本示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
当你使用 pdm run test_script.py
, 运行它时,PDM 会创建一个临时环境,并安装指定的依赖项,然后运行脚本:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
--reuse-env
选项。
你还可以在脚本元数据中添加 [tool.pdm]
部分来配置 PDM。例如:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
可以阅读这个 规范 以获取更多详细信息。
用户脚本#
PDM 还支持在可选的 [tool.pdm.scripts]
部分中定义自定义脚本快捷方式。
与 [project.scripts]
混淆?
在 pyproject.toml
中有另一个字段 [project.scripts]
, 并且这些脚本也可以使用 pdm run
来调用。它用于定义随包安装的控制台脚本入口点。因此,可执行文件只能在项目本身安装到环境中后才能运行。也就是说,你必须设置 distribution = true
。
相比之下, [tool.pdm.scripts]
定义了一些在你的项目中要运行的任务。无论 distribution
是 true
还是 false
,它对项目都有效。 这些任务主要用于开发和测试目的,并且支持更多类型和设置,这将在后面展示。你可以将它视为 Makefile
的替代品。它不需要项目被安装,但需要存在一个 pyproject.toml
文件。
查看关于 [project.scripts]
的更多解释。
然后,你可以运行 pdm run <script_name>
以在你的 PDM 项目的上下文中调用脚本。例如:
1 2 |
|
然后在终端中:
1 2 |
|
随后的参数将附加到命令中:
1 2 |
|
像 Yarn 一样的脚本快捷方式
内置的快捷方式使所有脚本都可用作根命令,只要脚本不与任何内置或插件贡献的命令冲突。
换句话说,如果你有一个 start
脚本,你可以同时运行 pdm run start
和 pdm start
。
但是如果你有一个 install
脚本,只有 pdm run install
会运行它,pdm install
仍然会运行内置的 install
命令。
PDM 支持 4 种类型的脚本:
cmd
#
纯文本脚本被视为普通命令,或者你可以明确地指定它:
1 2 |
|
在某些情况下,例如想要在参数之间添加注释时,它可能更方便。 要将命令指定为数组而不是字符串,请执行以下操作:
1 2 3 4 5 6 7 |
|
shell
#
Shell 脚本可用于运行更多与 Shell 相关的任务,例如管道和输出重定向。
这基本上是通过 subprocess.Popen()
和 shell=True
运行的:
1 2 |
|
call
#
脚本也可以定义为调用形式为 <module_name>:<func_name>
的 Python 函数:
1 2 |
|
函数可以提供文字参数:
1 2 |
|
composite
#
此脚本可执行其他已定义的脚本:
1 2 3 4 |
|
运行 pdm run all
将首先运行 lint
,然后如果 lint
成功,则运行 test
。
在版本 2.13.0 中添加
要覆盖默认行为并在失败后继续执行剩余的脚本,
将 keep_going
选项设置为 true
:
1 2 3 4 5 |
|
如果 keep_going
设置为 true
,复合脚本的返回代码要么是 '0'(如果所有都成功了),要么是最后一个失败的个别脚本的代码。
你还可以为调用的脚本提供参数:
1 2 3 4 |
|
Note
在命令行上传递的参数将传递给每个被调用的任务。
您还可以使用 composite
脚本来组合多个命令:
1 2 3 4 5 |
|
脚本选项#
env
#
当前 Shell 中设置的所有环境变量都可以被 pdm run
看到,并在执行时展开。
此外,你还可以在你的 pyproject.toml
中定义一些固定的环境变量:
1 2 3 |
|
注意我们如何使用 TOML 的语法 定义一个复合字典。
关于环境变量替换
脚本规范中的变量可以在所有脚本类型中进行替换。
在 cmd 脚本中,所有平台上只支持 ${VAR}
语法,但在 shell
脚本中,语法是依赖于平台的。
例如,Windows cmd 使用 %VAR%
,而 bash 使用 $VAR
。
Note
在复合任务级别指定的环境变量将覆盖调用任务定义的环境变量。
env_file
#
你还可以将所有环境变量存储在一个 dotenv 文件中,并让 PDM 读取它:
1 2 3 |
|
dotenv 文件中的变量不会覆盖任何现有的环境变量。 如果你希望 dotenv 文件覆盖现有的环境变量,请使用以下命令:
1 2 3 |
|
环境变量加载顺序
从不同源加载的环境变量,按以下顺序加载:
- 操作系统环境变量
- 项目环境,如
PDM_PROJECT_ROOT
,PATH
,VIRTUAL_ENV
, etc - 由
env_file
指定的 Dotenv 文档 env
指定的环境变量映射
来自后一个源的环境变量将覆盖来自前一个源的环境变量。 在复合任务级别指定的 dotenv 文件将覆盖调用任务定义的 dotenv 文件。
环境变量可以包含对之前加载的源中的另一个环境变量的引用,例如:
1 2 |
|
FOO=hello-42
. The reference can also contain a default value with the syntax ${VAR:-default}
.
working_dir
#
在版本 2.13.0 中添加
你可以为脚本设置当前工作目录:
1 2 3 |
|
相对路径将相对于项目根目录解析。
site_packages
#
为了确保运行环境与外部 Python 解释器正确隔离,
除非以下任一条件成立,否则不会将所选解释器的 site-packages
加载到 sys.path
中:
- 可执行文件来自
PATH
,但不在__pypackages__
文件夹内。 -s/--site-packages
标志跟随pdm run
.- 在脚本表或全局设置键
_
中有site_packages = true
。
请注意,如果启用了 PEP 582(不带 pdm run
前缀),则始终会加载 site-packages
。
共享选项#
如果你希望 pdm run
运行的所有任务共享选项,
你可以将它们写在 [tool.pdm.scripts]
表中的特殊键 _
下:
1 2 3 4 |
|
此外,在任务中,PDM_PROJECT_ROOT
环境变量将被设置为项目根目录。
参数占位符#
默认情况下,所有用户提供的额外参数都简单地附加到命令(或对于 composite
任务的所有命令)。
如果你想更多地控制用户提供的额外参数,你可以使用 {args}
占位符。
它适用于所有脚本类型,并且将为每个脚本适当地插入:
1 2 3 4 |
|
将生成以下插值(这些不是真正的脚本,只是为了说明插值):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
如果需要,你可以提供默认值,如果未提供用户参数,则将使用该默认值:
1 2 |
|
将产生以下结果:
1 2 3 4 |
|
Note
一旦检测到占位符,就不再附加参数了。
这对于 composite
脚本很重要,
因为如果在子任务中检测到占位符,
那么所有子任务都不会附加参数,
你需要显式地将占位符传递给每个需要的嵌套命令。
Note
call
脚本不支持 {args}
占位符,
因为它们可以直接访问 sys.argv
来处理这种复杂情况和更多情况。
{pdm}
占位符#
有时你可能有多个 PDM 安装,或者 pdm
安装了不同的名称。
这可能发生在 CI/CD 情况下,或者在不同的存储库中使用不同的 PDM 版本时。
为了使你的脚本更加健壮,你可以使用 {pdm}
来使用执行脚本的 PDM 入口点。
这将扩展为 {sys.executable} -m pdm
。
1 2 |
|
将产生以下输出:
1 2 3 4 5 |
|
Note
虽然上面的示例使用了 PDM 2.8,但此功能是在 2.10 系列中引入的,并且仅用于展示。
显示脚本列表#
使用 pdm run --list/-l
来显示可用脚本快捷方式的列表:
1 2 3 4 5 6 7 8 |
|
你可以添加一个带有脚本描述的 help
选项,它将显示在上述输出的 Description
列中。
Note
以下划线(_
)开头的任务被视为内部(辅助...)任务,并且不会在列表中显示。
前后脚本#
与 npm
类似,PDM 还支持通过前后脚本进行任务组合,前脚本将在给定任务之前运行,后脚本将在之后运行。
1 2 3 4 |
|
在此示例中,pdm run compress
将按顺序运行所有这 3 个脚本。
管道快速失败
在前 - 自身 - 后脚本的管道中,失败将取消后续执行。
钩子脚本#
在某些情况下,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
: 在运行脚本调用之前运行一次post_run
: 在运行脚本调用之后运行一次
Note
钩子脚本无法接收任何参数。
避免名称冲突
如果在 [tool.pdm.scripts]
表中存在一个 install
脚本,
则 pre_install
脚本可以同时被 pdm install
和 pdm run install
触发。
因此,建议不要使用保留的名称。
Note
复合任务也可以具有前后脚本。 调用的任务将运行自己的前后脚本。
跳过脚本#
有时,希望运行一个脚本,但不运行其钩子或前后脚本,
这时候可以使用 --skip=:all
来禁用所有钩子、前后脚本。
还有 --skip=:pre
和 --skip=:post
,
分别允许跳过所有 pre_*
钩子和所有 post_*
钩子。
还可能需要前脚本但不需要后脚本,
或者需要复合任务的所有任务,除了一个。
对于这些用例,可以使用更精细的 --skip
参数,
该参数接受要排除的任务或钩子名称的列表。
1 |
|
此命令将运行 my-composite
任务,并跳过 pre_task1
钩子以及 task2
及其钩子。
你还可以在 PDM_SKIP_HOOKS
环境变量中提供跳过列表,
但只要提供了 --skip
参数,它就会被覆盖。
关于钩子和前后脚本行为的更多详细信息,请参阅 专用钩子页面.