Youngkwang Yang한국어
Contents

pylock.toml, a standard lock file for Python

Python

pip now has a command that creates a lock file. Run it for requests and it writes a file named pylock.toml.

$ pip lock requests -o pylock.toml

pylock.toml is the standard Python lock-file format defined by PEP 751. Poetry has used poetry.lock, pipenv Pipfile.lock, PDM pdm.lock, and uv uv.lock, with no guarantee that another tool can read the same file. PEP 751 standardizes the format that tools write and consume.1

pip can create the file with pip lock and install it with pip install -r pylock.toml. Both features are still experimental. The pip behavior described below is from 26.1.2.

Lock formats differed by tool

A lock file records not only the package versions to install but the actual distribution files to use, so development, CI, and production can install the same package set from it.

requirements.txt has long been used in place of a lock file. pip freeze records the versions installed in the current environment, and adding hashes can restrict which files pip accepts. Hashes are not included by default and require an option, so users may not realize the difference.

Tools such as Poetry and uv covered what requirements files were missing with their own lock formats, but a different format cannot be assumed to work in another tool. With uv’s current adoption this may not look like a major problem, but the real cost is probably on the side that consumes lock files. Dependabot and deployment platforms need separate support for each format when there is no common standard. PEP 751 is meant to reduce that maintenance cost.

The pylock.toml structure

The file created above contains requests and its dependencies. Here is an excerpt.

lock-version = "1.0"
created-by = "pip"

[[packages]]
name = "certifi"
version = "2026.6.17"

[[packages.wheels]]
name = "certifi-2026.6.17-py3-none-any.whl"
url = "https://files.pythonhosted.org/packages/.../certifi-2026.6.17-py3-none-any.whl"

[packages.wheels.hashes]
sha256 = "2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db"

[[packages]]
name = "requests"
version = "2.34.2"

[[packages.wheels]]
name = "requests-2.34.2-py3-none-any.whl"
url = "https://files.pythonhosted.org/packages/.../requests-2.34.2-py3-none-any.whl"

[packages.wheels.hashes]
sha256 = "2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0"

A requirements file usually records a package name and version. This file also records, under each [[packages]], the wheel url to install and the hashes to verify it. Instead of choosing files for that version from the index again, an installer selects a compatible file recorded in the lock.

These are only the basic fields. The standard can use markers to cover multiple environments in one file, and record sdists, VCS sources, local directories, and so on.. See the official example for the details.

The standard install model and pip today

Pip prints an experimental-feature warning both when it creates a lock file and when it installs from one.

$ pip lock requests -o pylock.toml
WARNING: pip lock is currently an experimental command.
  It may be removed/changed in a future release without prior warning.

$ pip install --dry-run -r pylock.toml
WARNING: Using pylock.toml as a requirements source is an experimental feature.
  It may be removed/changed in a future release without prior warning.
Collecting requests==2.34.2 (from pylock.toml)
Would install requests-2.34.2 ...

The install model in PEP 751 and pip’s current implementation are different.

The PEP 751 installation procedure does not search for another combination of package versions. An installer checks environment markers and the Python version, selects the matching package entries and files, verifies their size and hashes, and installs them. The versions and artifacts were already chosen when the lock file was created.

As of pip 26.1, entries from pylock.toml go into the existing resolver like any other requirements input, but each package’s candidates are limited to the sdist and wheels recorded in the lock file. The resolver still runs, but it does not search an index for other versions and distribution files. The pip maintainers describe this behavior in the -r pylock.toml issue.

PEP 751 specpylock.tomlcheck environmentlocked artifactverify, installpip 26.1pylock.tomlrequirements inputexisting resolverlocked artifactinstall

In a direct pip install --dry-run measurement, pylock.toml was faster.2 Current pip 26.1 still sends both inputs through the existing resolver, so the difference cannot be attributed to whether the resolver runs. pylock may leave a smaller search space because candidates are limited to the recorded files, but with this many variables, this measurement alone cannot tell where the speed difference comes from.

What pip supports today

PEP 751 is Final, but pip’s support for pip lock and -r pylock.toml is still experimental. The commands and features pip uses to provide the specification may still change.

At present, pip lock only creates a lock file for the Python version and platform where it runs. Its help text says this directly.

The generated lock file is only guaranteed to be valid for
the current python version and platform.

The standard can use environments, extras, and dependency-groups to represent multiple environments and install choices in one file. pip cannot create a multiplatform lock yet, and it does not emit extras or dependency-group information. Its installation interface cannot select extras or dependency groups from a lock file either. The current limits are tracked in the pip lock issue and the -r pylock.toml issue.

The format also distinguishes information it can record from information an installer may use.

  • [[packages.dependencies]] can record relationships between packages. It is for auditing and analysis, and installers must not use it to select packages.
  • Build requirements needed for an sdist are not locked. PEP 751 left that to a later PEP.
  • Requirements-file features such as -r includes, environment-variable expansion, and --index-url are not part of pylock. A requirements file is input to resolution, while pylock records its result.

References

Footnotes

  1. This wasn’t the first attempt at a standard. PEP 665 (2021) was rejected for supporting wheels only. PEP 751, a broader spec that also covers other sources like sdists, was accepted in its place. See PEP 665.

  2. With a warm cache, the median of 12 runs was 276 ms for the requirements file and 183 ms for pylock.toml. This used --dry-run, so it did not include installing the files and does not represent E2E install time.