Skip to content

Commit e5711ba

Browse files
committed
Start of pylock.toml PEP 751 support.
1 parent 9e706bd commit e5711ba

File tree

8 files changed

+682
-0
lines changed

8 files changed

+682
-0
lines changed

docs/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ credentials
6161
shell
6262
docker
6363
scripts
64+
pylock
6465
advanced
6566
diagnose
6667
changelog

docs/pylock.md

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# PEP 751 pylock.toml Support
2+
3+
Pipenv supports [PEP 751](https://peps.python.org/pep-0751/) pylock.toml files, which provide a standardized format for recording Python dependencies to enable installation reproducibility.
4+
5+
## What is pylock.toml?
6+
7+
The pylock.toml file is a standardized lock file format introduced in PEP 751. It is designed to be:
8+
9+
- Human-readable and machine-generated
10+
- Secure by default (includes file hashes)
11+
- Able to support both single-use and multi-use lock files
12+
- Compatible across different Python packaging tools
13+
14+
## Using pylock.toml with Pipenv
15+
16+
Pipenv can automatically detect and use pylock.toml files in your project. When both a Pipfile.lock and a pylock.toml file exist, Pipenv will prioritize the pylock.toml file.
17+
18+
### Reading pylock.toml Files
19+
20+
When you run commands like `pipenv install` or `pipenv sync`, Pipenv will check for a pylock.toml file in your project directory. If found, it will use the dependencies specified in the pylock.toml file instead of Pipfile.lock.
21+
22+
Pipenv looks for pylock.toml files in the following order:
23+
1. A file named `pylock.toml` in the project directory
24+
2. A file matching the pattern `pylock.*.toml` in the project directory
25+
26+
### Example pylock.toml File
27+
28+
Here's a simplified example of a pylock.toml file:
29+
30+
```toml
31+
lock-version = '1.0'
32+
environments = ["sys_platform == 'win32'", "sys_platform == 'linux'", "sys_platform == 'darwin'"]
33+
requires-python = '>=3.8'
34+
extras = []
35+
dependency-groups = []
36+
default-groups = []
37+
created-by = 'pipenv'
38+
39+
[[packages]]
40+
name = 'requests'
41+
version = '2.28.1'
42+
requires-python = '>=3.7'
43+
44+
[[packages.wheels]]
45+
name = 'requests-2.28.1-py3-none-any.whl'
46+
upload-time = '2022-07-13T14:00:00Z'
47+
url = 'https://files.pythonhosted.org/packages/ca/91/6d9b8ccacd0412c08820f72cebaa4f0c61441f4AE7b7338a82051330d70/requests-2.28.1-py3-none-any.whl'
48+
size = 61805
49+
hashes = {sha256 = 'b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7'}
50+
```
51+
52+
## Benefits of Using pylock.toml
53+
54+
- **Standardization**: pylock.toml is a standardized format that can be used by multiple Python packaging tools.
55+
- **Security**: pylock.toml includes file hashes by default, making it more secure against supply chain attacks.
56+
- **Flexibility**: pylock.toml can support both single-use and multi-use lock files, allowing for more complex dependency scenarios.
57+
- **Interoperability**: pylock.toml can be used by different tools, reducing vendor lock-in.
58+
59+
## Limitations
60+
61+
- Currently, Pipenv only supports reading pylock.toml files, not writing them.
62+
- Some advanced features of pylock.toml, such as environment markers for extras and dependency groups, are not fully supported yet.
63+
64+
## Future Plans
65+
66+
In future releases, Pipenv plans to add support for:
67+
68+
- Writing pylock.toml files
69+
- Full support for environment markers for extras and dependency groups
70+
- Converting between Pipfile.lock and pylock.toml formats

examples/pylock.toml

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
lock-version = '1.0'
2+
environments = ["sys_platform == 'win32'", "sys_platform == 'linux'", "sys_platform == 'darwin'"]
3+
requires-python = '>=3.8'
4+
extras = []
5+
dependency-groups = []
6+
default-groups = []
7+
created-by = 'pipenv'
8+
9+
[[packages]]
10+
name = 'requests'
11+
version = '2.28.1'
12+
requires-python = '>=3.7'
13+
14+
[[packages.wheels]]
15+
name = 'requests-2.28.1-py3-none-any.whl'
16+
upload-time = '2022-07-13T14:00:00Z'
17+
url = 'https://files.pythonhosted.org/packages/ca/91/6d9b8ccacd0412c08820f72cebaa4f0c61441f4AE7b7338a82051330d70/requests-2.28.1-py3-none-any.whl'
18+
size = 61805
19+
hashes = {sha256 = 'b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7'}
20+
21+
[[packages]]
22+
name = 'urllib3'
23+
version = '1.26.12'
24+
requires-python = '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4'
25+
26+
[[packages.wheels]]
27+
name = 'urllib3-1.26.12-py2.py3-none-any.whl'
28+
upload-time = '2022-09-08T14:30:00Z'
29+
url = 'https://files.pythonhosted.org/packages/6f/de/5be2e3eed8426f871b170663333a0f627fc2924cc386cd41a2d2d3f1699/urllib3-1.26.12-py2.py3-none-any.whl'
30+
size = 141862
31+
hashes = {sha256 = 'b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997'}
32+
33+
[[packages]]
34+
name = 'certifi'
35+
version = '2022.9.24'
36+
37+
[[packages.wheels]]
38+
name = 'certifi-2022.9.24-py3-none-any.whl'
39+
upload-time = '2022-09-24T18:00:00Z'
40+
url = 'https://files.pythonhosted.org/packages/1d/38/fa96a426e0c0e68aabc68e896584b83ad1eec779265a028e156ce509630/certifi-2022.9.24-py3-none-any.whl'
41+
size = 161915
42+
hashes = {sha256 = '0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14'}
43+
44+
[[packages]]
45+
name = 'charset-normalizer'
46+
version = '2.1.1'
47+
requires-python = '>=3.6.0'
48+
49+
[[packages.wheels]]
50+
name = 'charset_normalizer-2.1.1-py3-none-any.whl'
51+
upload-time = '2022-08-05T12:00:00Z'
52+
url = 'https://files.pythonhosted.org/packages/a1/34/44964211e5410b051e4b8d2869c470ae8a68ae274953b1c7de6d98bbcf94/charset_normalizer-2.1.1-py3-none-any.whl'
53+
size = 39673
54+
hashes = {sha256 = '83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f'}
55+
56+
[[packages]]
57+
name = 'idna'
58+
version = '3.4'
59+
60+
[[packages.wheels]]
61+
name = 'idna-3.4-py3-none-any.whl'
62+
upload-time = '2022-08-12T15:00:00Z'
63+
url = 'https://files.pythonhosted.org/packages/fc/34/3030de6f1370931b9dbb4dad48f6ab1015ab1d32447850b9fc94e60097be/idna-3.4-py3-none-any.whl'
64+
size = 61545
65+
hashes = {sha256 = '90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2'}
66+
67+
[tool.pipenv]
68+
generated_from = "Pipfile.lock"
69+
generation_date = "2025-04-25T04:20:00Z"

news/7751.feature.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added support for reading PEP 751 pylock.toml files. When both a Pipfile.lock and a pylock.toml file exist, Pipenv will prioritize the pylock.toml file.

pipenv/project.py

+21
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
proper_case,
5858
)
5959
from pipenv.utils.locking import atomic_open_for_write
60+
from pipenv.utils.pylock import PylockFile, find_pylock_file
6061
from pipenv.utils.project import get_default_pyproject_backend
6162
from pipenv.utils.requirements import normalize_name
6263
from pipenv.utils.shell import (
@@ -793,6 +794,19 @@ def _pipfile(self):
793794
pf = ReqLibPipfile.load(self.pipfile_location)
794795
return pf
795796

797+
@property
798+
def pylock_location(self):
799+
"""Returns the location of the pylock.toml file, if it exists."""
800+
pylock_path = find_pylock_file(self.project_directory)
801+
if pylock_path:
802+
return str(pylock_path)
803+
return None
804+
805+
@property
806+
def pylock_exists(self):
807+
"""Returns True if a pylock.toml file exists."""
808+
return self.pylock_location is not None
809+
796810
@property
797811
def lockfile_location(self):
798812
return f"{self.pipfile_location}.lock"
@@ -803,6 +817,13 @@ def lockfile_exists(self):
803817

804818
@property
805819
def lockfile_content(self):
820+
"""Returns the content of the lockfile, checking for pylock.toml first."""
821+
if self.pylock_exists:
822+
try:
823+
pylock = PylockFile.from_path(self.pylock_location)
824+
return pylock.convert_to_pipenv_lockfile()
825+
except Exception as e:
826+
err.print(f"[bold yellow]Error loading pylock.toml: {e}[/bold yellow]")
806827
return self.load_lockfile()
807828

808829
def get_editable_packages(self, category):

0 commit comments

Comments
 (0)