尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

PDM 生命周期与 Hooks 深度指南:用信号、用户脚本与 --skip 精确编排你的 Python 项目流程

发布时间:2026/9/16 11:39:39

资讯中心
01
ARTICLE

PDM 生命周期与 Hooks 深度指南:用信号、用户脚本与 --skip 精确编排你的 Python 项目流程

PDM 生命周期与 Hooks 深度指南:用信号、用户脚本与 --skip 精确编排你的 Python 项目流程
PDM 生命周期与 Hooks 深度指南用信号、用户脚本与 --skip 精确编排你的 Python 项目流程【免费下载链接】pdmA modern Python package and dependency manager supporting the latest PEP standards项目地址: https://gitcode.com/GitHub_Trending/pd/pdm作为任何 Python 交付物你的项目都会经历一个典型的 Python 项目生命周期。PDM 为这些阶段提供了对应的内置命令并在其上挂载了可扩展的 hooks钩子一方面插件可以监听同名的事件信号pdm.signals另一方面开发者也可以定义同名自定义脚本。此外pre_invoke信号会在任意命令被调用之前发出允许插件提前修改项目或命令行选项。本指南将以 docs/usage/hooks.md 为骨架结合 PDM 仓库源码信号定义、HookManager 实现、命令调用链与测试用例为你完整梳理内置命令的三阶段生命周期、用户脚本的 hook 嵌套规则以及按需跳过 hooks 与任务的方法。生命周期总览三个内置阶段PDM 的内置命令被划分为三个阶段初始化阶段Initializationpdm init依赖管理阶段Dependencies Managementpdm lock/pdm sync/pdm install/pdm add/pdm remove/pdm update外加特殊的pdm use发布阶段Publicationpdm build/pdm publish在每个阶段PDM 都会在关键节点发出信号signal并顺带执行同名用户脚本见下文用户脚本一节。执行过程遵循fail-fast原则一旦某个步骤包括 hooks失败后续步骤立即停止。初始化阶段Initialization初始化在整个项目生命周期中只应发生一次通过运行pdm init以交互方式填充pyproject.toml将一个既有目录初始化为 PDM 项目。该阶段触发以下 hookpost_init流程如下从源码来看post_init在pdm init命令完成项目初始化后发出位于 src/pdm/cli/commands/init.pyinitialize_git之后调用hooks.try_emit(post_init)。信号签名中post_init的唯一参数是project对象见 src/pdm/signals.py适合在项目创建后执行补充操作例如自动生成.gitignore、初始化版本控制系统或打印欢迎信息。依赖管理阶段Dependencies Management依赖管理是开发者日常工作的核心覆盖以下任务lock根据pyproject.toml中的需求计算锁文件sync依据锁文件同步新增/移除/更新PEP 582 的__pypackages__包并以可编辑模式安装当前项目add新增依赖remove移除依赖上述任务可直接通过以下命令执行命令行为pdm lock仅执行lock任务pdm sync仅执行sync任务pdm install执行sync必要时先lockpdm add追加依赖需求重新 lock 后再 syncpdm remove移除依赖需求重新 lock 后再 syncpdm update按最新版本重新 lock随后 sync这些命令触发如下 hookspre_installpost_installpre_lockpost_lock以pdm install为例其内部生命周期为 lock 阶段与 sync 阶段串行这一流程图与源码实现完全吻合lock 侧pdm lock在 src/pdm/cli/actions.py 调用hooks.try_emit(pre_lock, requirementsrequirements, dry_rundry_run)开始解析成功写出锁文件后再在 src/pdm/cli/actions.py 调用hooks.try_emit(post_lock, resolutionresult_repo.all_candidates, dry_rundry_run)。sync 侧do_sync在真正执行同步前后分别发出pre_install与post_installsrc/pdm/cli/actions.py两个信号都携带packages与dry_run参数。update 特例pdm update由于内部 lock 总是以dry_runTrue进行会手动发出pre_lock/post_lock并用hooks.skipping(pre_lock, post_lock)临时屏蔽内部do_lock的信号避免重复触发src/pdm/cli/commands/update.py。这是 HookManager 提供skipping上下文管理器的一个典型用例。add 特例pdm add在写锁文件后手动发出post_locksrc/pdm/cli/commands/add.py随后再执行do_sync。各命令与 hooks 的对应关系在测试中也有完整清单可查见 tests/cli/test_hooks.py 的KNOWN_COMMAND_HOOKS命令触发 hooksaddpre_lock,post_lockinstallpre_install,post_install,pre_lock,post_locklockpre_lock,post_locksyncpre_install,post_installremovepre_lock,post_lockupdatepre_install,post_install,pre_lock,post_lock测试test_pre_script_fail_fasttests/cli/test_hooks.py还验证了 fail-fast 行为当pre_install脚本exit(1)时post_install不会执行同步器甚至不会被调用synchronize.assert_not_called()。切换 Python 版本依赖管理中的一个特例使用pdm use切换当前 Python 版本时会以新解释器对象为参数发出post_use信号。在源码中do_use在选定并保存新的解释器后执行hooks.try_emit(post_use, pythonselected_python)src/pdm/cli/commands/use.pypost_use信号的参数为project与pythonPythonInfo类型见 src/pdm/signals.py。这在切换到新解释器后重写 shebang、更新 IDE 配置等场景中非常有用。发布阶段Publication当你的包/库准备好对外发布时需要执行以下发布任务build构建/编译需要构建的产物并将一切打包为 Python 分发包sdist、wheelupload上传/发布到远程 PyPI 索引对应命令为pdm buildpdm publish它们触发以下 hookspre_publishpost_publishpre_buildpost_buildpdm publish的完整生命周期如下upload阶段目前没有独立的上传 hook预留给未来扩展对应源码pdm publish在[src/pdm/cli/commands/publish/__init__.py](https://link.gitcode.com/i/aba61bdeff7e0f7341e11cea1d2c10ba)中先发出pre_publish构建/上传完成后发出post_publish。pdm build在 src/pdm/cli/commands/build.py 中于构建前后分别发出pre_build携带dest与config_settings与post_build携带artifacts与config_settings。同样遵循 fail-fast任一环节包括 hooks失败流程立即终止。测试KNOWN_COMMAND_HOOKS也确认publish依次触发pre_publish、pre_build、post_build、post_publish四个信号tests/cli/test_hooks.py。用户脚本User Scripts可编排的自定义 hooks在安装与发布之间你很可能需要反复执行一些固定任务整理代码、lint、测试……PDM 为此提供了 用户脚本user scripts机制让你定义自己的任务与阶段。与 hooks 配合的关键规则如下每个用户脚本都可以定义对应的pre_*与post_*脚本复合脚本composite同样适用每次pdm run执行都会触发pre_run与post_run两个 hook每个脚本执行都会触发pre_script与post_script两个 hook。考虑如下scripts定义[tool.pdm.scripts] pre_script post_script pre_test post_test test pre_composite post_composite composite {composite [test]}执行pdm run test的完整生命周期如下而执行pdm run composite复合脚本内部嵌套test时hook 会逐层递归触发这一嵌套语义在源码中非常清晰src/pdm/cli/commands/run.py 的run()方法对每个任务依次执行发出pre_script→ 若存在pre_{command}脚本且未被跳过则先执行 → 执行主任务 → 若存在post_{command}脚本则执行 → 发出post_script复合脚本则通过递归调用self.run(cmd, ...)展开子任务因此每进入一层任务都会重新触发该层的pre_script/post_script。同时脚本内部任何一步返回非零退出码都会立即中止fail-fast。tests/cli/test_hooks.py中的test_pre_and_post_scripts与test_composite_runs_all_hooks精确验证了上述调用顺序tests/cli/test_hooks.py单脚本执行顺序为pre_run → pre_script → pre_test → test → post_test → post_script → post_run复合脚本test [first, second]则会展开为pre_run → pre_script → pre_test → (pre_script → pre_first → first → post_script) → (pre_script → second → post_second → post_script) → post_test → post_script → post_run。所有pre_run/post_run/pre_script/post_script信号都携带project、script脚本名与args命令行参数三个参数供监听方获取上下文见 src/pdm/signals.py。按需跳过--skip 选项与 PDM_SKIP_HOOKS对于任意内置命令以及自定义用户脚本都可以通过--skip短选项-k控制哪些任务与 hook 需要执行。--skip接受以逗号分隔的 hook/任务名称列表同时支持三个预定义快捷方式快捷方式含义:all跳过所有 hooks:pre跳过所有pre_*hooks:post跳过所有post_*hooks你也可以通过环境变量PDM_SKIP_HOOKS提供跳过列表但只要在命令行提供了--skip参数环境变量即被覆盖命令行优先。基于前面的脚本块运行pdm run --skip:pre,post_test composite将得到如下精简后的生命周期可以看到所有pre_*hook/脚本、post_test以及pre_run均被跳过仅保留test、各层的post_script/post_composite与post_run。--skip的底层实现由HookManager支撑src/pdm/cli/hooks.py构造时接收skip列表skip_all/skip_pre/skip_post三个属性分别对应:all/:pre/:postshould_run(name)综合判断某名称是否应执行不在跳过列表、未被:all屏蔽、且不被:pre/:post按前缀拦截try_emit(name, **kwargs)仅在should_run通过时才真正向pdm_signals发送信号skipping(*names)是上下文管理器可在代码片段内临时追加跳过项并在退出时恢复pdm update内部正是用它避免重复触发 lock 信号。命令行的解析位于 src/pdm/cli/options.py-k/--skip选项使用split_lists(,)按逗号拆分可多次传递如--skip pre_test --skip post_first其默认值由from_splitted_env(PDM_SKIP_HOOKS, ,)从环境变量读取。tests/cli/test_hooks.py的test_skip_option参数化用例覆盖了逗号列表、多次-k/--skip混用等多种写法tests/cli/test_hooks.pytest_skip_option_default_from_env则验证了PDM_SKIP_HOOKS的解析规则空白项会被忽略tests/cli/test_hooks.py。pre_invoke所有命令的前置监听点除了各阶段信号外pre_invoke在任何命令被调用前发出src/pdm/core.py携带command命令名与options解析后的参数命名空间两个参数。这是插件拦路级挂载点可以借此检查参数、动态修改项目配置或在命令真正执行前完成准备工作。注意pre_invoke由core.py统一发出因此它同样受--skip规则约束。小结与最佳实践PDM 的生命周期与 hooks 机制可以归纳为三层协作内置命令信号post_init、pre_lock/post_lock、pre_install/post_install、post_use、pre_build/post_build、pre_publish/post_publish、pre_run/post_run、pre_script/post_script、pre_invoke完整定义集中在 src/pdm/signals.py。同名用户脚本在[tool.pdm.scripts]中定义pre_task/task/post_task即可让pdm run task、甚至pdm install、pdm build等内置命令自动调用实现零成本挂载的预处理/后处理逻辑。跳过机制--skip/-k与PDM_SKIP_HOOKS让你按需裁剪执行链路特别适合 CI 中复用同一份脚本定义却需要不同执行策略的场景。实战建议把 lint、测试、类型检查、构建校验等固定动作写成带pre_*/post_*的同名脚本利用 fail-fast 保证任何一步失败都不会继续污染后续产物在发布流水线中用pdm publish --skip:pre之类的方式临时绕过不必要的检查或借助PDM_SKIP_HOOKS实现不同环境的差异化行为从而让项目生命周期真正做到可编排、可复用、可控制。【免费下载链接】pdmA modern Python package and dependency manager supporting the latest PEP standards项目地址: https://gitcode.com/GitHub_Trending/pd/pdm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。