The Python lock file, pylock.toml, records exact dependencies your project needs so installs come out the same every time. It isn’t the first of its kind, though—most modern package tools already define their own lock formats. This has led to a fragmented landscape where reproducibility is often limited to a single tool’s workflow, with each ecosystem effectively speaking its own lock file dialect.
From this fragmentation comes a shared standard: pylock.toml. Defined in PEP 751, pylock.toml standardizes reproducible dependency management across different tools, eliminating vendor lock-in and fragmented requirements.txt workflows. Its purpose is to capture a fully resolved set of dependencies in a format that every tool can understand.
But doesn’t requirements.txt already do this? Not exactly. requirements.txt isn’t so much a standardized specification as an implementation detail of pip. The Requirements File Format specifies that “requirements files serve as a list of items to be installed by pip, when using pip install.”
Still, both files exist to enumerate dependencies that should end up in an environment. The difference lies in how that information is represented, either as tool-specific instructions or as a structured, tool-agnostic record of a resolved state:
| If you… | pylock.toml |
requirements.txt |
|---|---|---|
Want to share a single lock file with teammates who use different tools (uv, pip, pdm) |
✅ | — |
| Need reproducible, hash-verified installs in CI | ✅ | — |
| Want to install without resolving dependencies on the machine | ✅ | — |
Need to pass pip flags like --index-url or --no-deps from the file |
— | ✅ |
| Want to jot down a couple of dependencies for a quick script by hand | — | ✅ |
This means pylock.toml is the better choice when you care about reproducibility across tools, the integrity of installed artifacts, or a single shared source of truth that can be consumed in different workflows. It’s especially useful in team settings or continuous integration (CI) pipelines where consistency matters more than manual control.
In contrast, requirements.txt remains a good fit for lightweight workflows, quick experiments, or cases where direct control over pip behavior and flags is more important than cross-tool interoperability or strict locking guarantees.
Get Your Code: Click here to download the free sample code you’ll use to generate, inspect, and reuse pylock.toml lock files across pip, uv, and pdm.
Take the Quiz: Test your knowledge with our interactive “Tool-Agnostic Python Lock Files With PEP 751 and pylock.toml” quiz. You’ll receive a score upon completion to help you track your learning progress:
Interactive Quiz
Tool-Agnostic Python Lock Files With PEP 751 and pylock.tomlTest your understanding of Python lock files and PEP 751, from generating a pylock.toml with pip to installing it across uv and pdm.
Generating a Python Lock File With pip
With pip version 25.1 or newer, you can generate a pylock.toml file from a requirements.txt file using the experimental pip lock command. At its core, the pylock.toml will serve as a snapshot of every dependency pip would install.
Before you can use pip lock, you’ll need to define a small set of explicit dependencies in a standard requirements file:
requirements.txt
pandas
scipy
This file expresses only the top-level packages to be installed in your environment. It doesn’t describe how pip resolves those packages or what additional dependencies they need to work.
From here, you can have pip produce a fully resolved lock file:
$ python -m pip lock --requirement requirements.txt
WARNING: pip lock is currently an experimental command. It may be
⮑ removed/changed in a future release without prior warning.
Collecting pandas (from -r requirements.txt)
Downloading pandas-3.0.2-cp314-cp314-manylinux_2_24_x86_64...
Collecting scipy (from -r requirements.txt)
Downloading scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64...
Collecting numpy>=2.3.3 (from pandas->-r requirements.txt)
Downloading numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64...
Collecting python-dateutil>=2.8.2 (from pandas->-r requirements.txt)
Downloading python_dateutil-2.9.0.post0-py2.py3-none-any...
Collecting six>=1.5 (from python-dateutil>=2.8.2->pandas->-r requirements.txt)
Downloading six-1.17.0-py2.py3-none-any.whl.metadata (1.7 kB)
This triggers the resolution process. pip builds a complete dependency graph starting from the explicit packages (pandas, scipy), resolves all required transitive dependencies, and downloads compatible versions for the current Python environment and platform.
This means the result contains both:
- Explicit dependencies: What you specified directly, like
pandasandscipy - Transitive dependencies: What those packages require to function, like
numpy
You can explore that split for yourself in the interactive graph below. Start with only pandas declared to see the full set it resolves to, then toggle scipy on and watch your two requested packages expand into a larger set of pinned dependencies:
Pay attention to numpy as you toggle scipy on and off. The install log above attributes numpy to pandas, but scipy depends on it too, so the resolver doesn’t lock it twice—it records a single, shared numpy entry that satisfies both. That’s exactly what a lock file does for you: it turns the short list of packages you asked for into the complete, deduplicated set that will actually be installed.
By capturing the complete dependency graph, pylock.toml enables reproducible and verifiable installations across environments.



