pdm-build-locked
pdm-build-locked is a pdm plugin to enabling reproducible installs of Python CLI tools – no breakage on dependency updates.
It achieves this by adding the pinned packages from PDM’s lockfile as additional optional dependency groups to the distribution metadata.
What it does
Normally, you would only be able to use your locked dependencies from your lockfile when using pdm in your dev environment.
This plugin enables using the lockfile in a deployment scenario.
Thus, your users may install your package as an exact reproduction of your dev environment lockfile by running:
pip install mypkg[locked]
So for mypkg
with optional-dependencies
group extras
you will end up with the following groups:
locked
extras-locked
To install both, you would run:
pip install mypkg[locked, extras-locked]
Hint
It achieves this by adding optional-dependencies groups that allow the user to opt-in to installing the dependencies that were pinned in the lockfile:
[locked]
- contains all default dependencies as pinned version from lockfilefor each optional-dependencies group
<group>
:[<group>-locked]
- contains optional dependencies for group <group> as pinned version from lockfile
When to use
To avoid misuse, we recommend deciding whether to use this plugin based on your project type:
CLI tool: If your package is a CLI tool that will be installed in an isolated virtualenv, for example using
pipx
.CLI tool and library: Recommended. Advise your users to only use the [locked] group when used as an executable (never in
pyproject.toml
dependencies!).Library only: Do not use.
Danger
The following example is highly discouraged and should never be used, as it will easily lead to dependency conflicts.
pyproject.toml
dependencies = [
my-library[locked]==1.1.1, # this will break your install sooner rather than later
some-other-library
]
Which plugin should I use?
PDM CLI Plugin
If you only care about reproducible installs after publishing your package, you may use the
Compatible package managers:
pdm only
Compatible build backends:
any PEP 517 compatible build backend (
setuptools
,flit-core
,pdm-backend
,`hatchling
etc.)
pipx install mypkg[locked]
Backend Plugin
If you want to be able to install your package from a local directory or a git repository, you need to use the
Compatible package managers:
any PEP 621 compatible package manager (
poetry
,flit
,pdm
,hatch
etc.)
Compatible build backends:
pdm-backend and hatchling
pipx install "mypkg[locked] @ git+https://github.com/myorg/mypkg"
PDM CLI plugin
The PDM CLI plugin replaces the pdm build
command with a locked build. Essentially it
edits your
pyproject.toml
and adds the additional optional-dependency groups with pins from the lockfile to ithides the file changes from git to avoid a dirty status on build
calls the actual
pdm build
commandrestores the original
pyproject.toml
The result is a wheel that is installable reproducibly, as it contains the exact dependencies you used to build it.
It only requires the user to add [locked]
on install.
Installing the plugin
To install the plugin, you need to run pdm install --plugins
.
Alternatively, you can activate the plugin globally by running pdm self add pdm-build-locked
.
Plugin registration
pyproject.toml
[tool.pdm]
plugins = [
"pdm-build-locked"
]
This registers the plugin with pdm.
Enabling the plugin
To enable locked builds, set the locked
entry in pyproject.toml
:
[tool.pdm.build] package-dir = "src" locked = trueThis will enable locked releases in the pipeline, as it also affects a basic
pdm build
call.
Hint
Locally, the following options also work.
run
pdm build --locked
set
PDM_BUILD_LOCKED
env var totrue
Build Backend Plugin
You can even use this plugin without PDM. This is enabled by build backend hooks.
Currently, both pdm-backend and hatchling are supported.
To set it up, a few configuration steps are required.
Lockfile configuration
Your lockfile must be configured with the include_metadata
strategy (pdm>=2.11
) and include locks for the
optional-dependencies groups you want to publish locked.
pyproject configuration
If you already have a [project.optional-dependencies]
section, skip this step.
Else, add the following to the start of your pyproject.toml
:
[project]
dynamic = ["optional-dependencies"]
buildsystem configuration
This step depends on the build-system you use and requires you to add the following to your pyproject.toml
.
pdm-backend
[build-system]
requires = ["pdm-backend", "pdm-build-locked"]
build-backend = "pdm.backend"
[tool.pdm.build]
locked = true
hatchling
[build-system]
requires = ["hatchling", "pdm-build-locked"]
build-backend = "hatchling.build"
[tool.hatch.metadata.hooks.build-locked]