hooks.mdβ’8.1 kB
# Lifecycle and Hooks
As any Python deliverable, your project will go through the different phases
of a Python project lifecycle and PDM provides commands to perform the expected tasks for those phases.
It also provides hooks attached to these steps allowing for:
- plugins to listen to the [signals][pdm.signals] of the same name.
- developers to define custom scripts with the same name.
Besides, `pre_invoke` signal is emitted before ANY command is invoked, allowing plugins to modify the project or options beforehand.
The built-in commands are currently split into 3 groups:
- the [initialization phase](#initialization)
- the [dependencies management](#dependencies-management).
- the [publication phase](#publication).
You will most probably need to perform some recurrent tasks between the installation and publication phases (housekeeping, linting, testing, ...)
this is why PDM lets you define your own tasks/phases using [user scripts](#user-scripts).
To provides full flexibility, PDM allows to [skip some hooks and tasks](#skipping) on demand.
## Initialization
The initialization phase should occur only once in a project lifetime by running the [`pdm init`](../reference/cli.md#init)
command to initialize an existing project (prompt to fill the `pyproject.toml` file).
They trigger the following hooks:
- [`post_init`][pdm.signals.post_init]
```mermaid
flowchart LR
subgraph pdm-init [pdm init]
direction LR
post-init{{Emit post_init}}
init --> post-init
end
```
## Dependencies management
The dependencies management is required for the developer to be able to work and perform the following:
- `lock`: compute a lock file from the `pyproject.toml` requirements.
- `sync`: synchronize (add/remove/update) PEP582 packages from the lock file and install the current project as editable.
- `add`: add a dependency
- `remove`: remove a dependency
All those steps are directly available with the following commands:
- [`pdm lock`](../reference/cli.md#lock): execute the `lock` task
- [`pdm sync`](../reference/cli.md#sync): execute the `sync` task
- [`pdm install`](../reference/cli.md#install): execute the `sync` task, preceded from `lock` if required
- [`pdm add`](../reference/cli.md#add): add a dependency requirement, re-lock and then sync
- [`pdm remove`](../reference/cli.md#remove): remove a dependency requirement, re-lock and then sync
- [`pdm update`](../reference/cli.md#update): re-lock dependencies from their latest versions and then sync
They trigger the following hooks:
- [`pre_install`][pdm.signals.pre_install]
- [`post_install`][pdm.signals.post_install]
- [`pre_lock`][pdm.signals.pre_lock]
- [`post_lock`][pdm.signals.post_lock]
```mermaid
flowchart LR
subgraph pdm-install [pdm install]
direction LR
subgraph pdm-lock [pdm lock]
direction TB
pre-lock{{Emit pre_lock}}
post-lock{{Emit post_lock}}
pre-lock --> lock --> post-lock
end
subgraph pdm-sync [pdm sync]
direction TB
pre-install{{Emit pre_install}}
post-install{{Emit post_install}}
pre-install --> sync --> post-install
end
pdm-lock --> pdm-sync
end
```
### Switching Python version
This is a special case in dependency management:
you can switch the current Python version using [`pdm use`](../reference/cli.md#use)
and it will emit the [`post_use`][pdm.signals.post_use] signal with the new Python interpreter.
```mermaid
flowchart LR
subgraph pdm-use [pdm use]
direction LR
post-use{{Emit post_use}}
use --> post-use
end
```
## Publication
As soon as you are ready to publish your package/library, you will require the publication tasks:
- `build`: build/compile assets requiring it and package everything into a Python package (sdist, wheel)
- `upload`: upload/publish the package to a remote PyPI index
All those steps are available with the following commands:
- [`pdm build`](../reference/cli.md#build)
- [`pdm publish`](../reference/cli.md#publish)
They trigger the following hooks:
- [`pre_publish`][pdm.signals.pre_publish]
- [`post_publish`][pdm.signals.post_publish]
- [`pre_build`][pdm.signals.pre_build]
- [`post_build`][pdm.signals.post_build]
```mermaid
flowchart LR
subgraph pdm-publish [pdm publish]
direction LR
pre-publish{{Emit pre_publish}}
post-publish{{Emit post_publish}}
subgraph pdm-build [pdm build]
pre-build{{Emit pre_build}}
post-build{{Emit post_build}}
pre-build --> build --> post-build
end
%% subgraph pdm-upload [pdm upload]
%% pre-upload{{Emit pre_upload}}
%% post-upload{{Emit post_upload}}
%% pre-upload --> upload --> post-upload
%% end
pre-publish --> pdm-build --> upload --> post-publish
end
```
Execution will stop at first failure, hooks included.
## User scripts
[User scripts are detailed in their own section](scripts.md) but you should know that:
- each user script can define a `pre_*` and `post_*` script, including composite scripts.
- each `run` execution will trigger the [`pre_run`][pdm.signals.pre_run] and [`post_run`][pdm.signals.post_run] hooks
- each script execution will trigger the [`pre_script`][pdm.signals.pre_script] and [`post_script`][pdm.signals.post_script] hooks
Given the following `scripts` definition:
```toml
[tool.pdm.scripts]
pre_script = ""
post_script = ""
pre_test = ""
post_test = ""
test = ""
pre_composite = ""
post_composite = ""
composite = {composite = ["test"]}
```
a `pdm run test` will have the following lifecycle:
```mermaid
flowchart LR
subgraph pdm-run-test [pdm run test]
direction LR
pre-run{{Emit pre_run}}
post-run{{Emit post_run}}
subgraph run-test [test task]
direction TB
pre-script{{Emit pre_script}}
post-script{{Emit post_script}}
pre-test[Execute pre_test]
post-test[Execute post_test]
test[Execute test]
pre-script --> pre-test --> test --> post-test --> post-script
end
pre-run --> run-test --> post-run
end
```
while `pdm run composite` will have the following:
```mermaid
flowchart LR
subgraph pdm-run-composite [pdm run composite]
direction LR
pre-run{{Emit pre_run}}
post-run{{Emit post_run}}
subgraph run-composite [composite task]
direction TB
pre-script-composite{{Emit pre_script}}
post-script-composite{{Emit post_script}}
pre-composite[Execute pre_composite]
post-composite[Execute post_composite]
subgraph run-test [test task]
direction TB
pre-script-test{{Emit pre_script}}
post-script-test{{Emit post_script}}
pre-test[Execute pre_test]
post-test[Execute post_test]
pre-script-test --> pre-test --> test --> post-test --> post-script-test
end
pre-script-composite --> pre-composite --> run-test --> post-composite --> post-script-composite
end
pre-run --> run-composite --> post-run
end
```
## Skipping
It is possible to control which task and hook runs for any built-in command as well as custom user scripts using the `--skip` option.
It accepts a comma-separated list of hooks/task names to skip
as well as the predefined `:all`, `:pre` and `:post` shortcuts
respectively skipping all hooks, all `pre_*` hooks and all `post_*` hooks.
You can also provide the skip list in `PDM_SKIP_HOOKS` environment variable
but it will be overridden as soon as the `--skip` parameter is provided.
Given the previous script block, running `pdm run --skip=:pre,post_test composite` will result in the following reduced lifecycle:
```mermaid
flowchart LR
subgraph pdm-run-composite [pdm run composite]
direction LR
post-run{{Emit post_run}}
subgraph run-composite [composite task]
direction TB
post-script-composite{{Emit post_script}}
post-composite[Execute post_composite]
subgraph run-test [test task]
direction TB
post-script-test{{Emit post_script}}
test --> post-script-test
end
run-test --> post-composite --> post-script-composite
end
run-composite --> post-run
end
```