@@ -35,10 +35,11 @@ community (PDM_, ``pip freeze``, pip-tools_, Poetry_, and uv_), there seems to
35
35
be an appetite for lock files in general.
36
36
37
37
Those tools also vary in what locking scenarios they support. For instance,
38
- ``pip freeze `` and pip-tools only generate lock files for the current
38
+ ``pip freeze `` and pip-tools only generate single-use lock files for the current
39
39
environment while PDM, Poetry, and uv can/try to lock for multiple environments
40
- at once. There's also concerns around the lack of secure defaults in the face of
41
- supply chain attacks (e.g. including hashes for files).
40
+ and use-cases at once. There's also concerns around the lack of secure defaults
41
+ in the face of supply chain attacks by some tools (e.g. including hashes for
42
+ files).
42
43
43
44
The lack of a standard also has some drawbacks. For instance, any tooling that
44
45
wants to work with lock files must choose which format to support, potentially
@@ -47,7 +48,7 @@ same for cloud providers who can do dependency installations on your behalf,
47
48
etc.). It also impacts portability between tools, which causes vendor lock-in.
48
49
By not having compatibility and interoperability it fractures tooling around
49
50
lock files where both users and tools have to choose what lock file format to
50
- use upfront and making it costly to use/switch to other formats (e.g. tooling
51
+ use upfront, making it costly to use/switch to other formats (e.g. tooling
51
52
around auditing a lock file). Rallying around a single format removes this
52
53
cost/barrier.
53
54
@@ -80,7 +81,9 @@ creating a lock file.
80
81
81
82
The data in the file should be consumable by tools not written in Python. This
82
83
allows for e.g. cloud hosting providers to write their own tool to perform
83
- installations in their preferred programming language.
84
+ installations in their preferred programming language. This introduces the
85
+ concept of *lockers * which write the lock file, and *installers * which install
86
+ from a lock file (they can be the same tool).
84
87
85
88
The file format should promote good security defaults. As the format is not
86
89
meant to be human-writable, this means having tools provide security-related
@@ -92,6 +95,24 @@ pip-tools_ and ``pip freeze`` emit). This means the file format specified by
92
95
this PEP can, at minimum, act as an export target for tools which have their own
93
96
internal lock file format.
94
97
98
+ Lock files can be *single-use * and *multi-use *. Single-use lock files are things
99
+ like ``requirements.txt `` files, which serve a single use-case/purpose (hence
100
+ why it isn't uncommon for a project to have multiple requirements files, each
101
+ for a diffetent use-case). Multi-use lock files represent multiple use-cases
102
+ within a single file, often expressed through
103
+ :ref: `extras <packaging:core-metadata-provides-extra >` and
104
+ :ref: `packaging:dependency-groups `. As such, this PEP supports additions to
105
+ :ref: `packaging:dependency-specifiers-environment-markers ` that allows for
106
+ specifying extras and dependency groups as appropriate. This allows
107
+ for a single lock file to support those cases. This not only cuts down on the
108
+ number of potential lock files, but it also makes it easier when a single
109
+ package is meant to be consistent across all use-cases (with multiple single-use
110
+ lock files it requires coordinating across multiple lock files). This support
111
+ also means that this PEP supports all package installation-related data that can
112
+ be recorded in a ``pyproject.toml `` file. The hope is that this support will
113
+ allow some tools to drop their internal lock file entirely and solely rely on
114
+ what this PEP specifies.
115
+
95
116
96
117
=============
97
118
Specification
@@ -139,6 +160,7 @@ recorded in a consistent order (if inspiration is desired, this PEP has tried to
139
160
write down keys in a logical order). As well, tools SHOULD sort arrays in
140
161
consistent order. Usage of inline tables SHOULD also be kept consistent.
141
162
163
+ .. File details
142
164
143
165
``lock-version ``
144
166
================
@@ -177,6 +199,41 @@ consistent order. Usage of inline tables SHOULD also be kept consistent.
177
199
(i.e. the minimum viable Python version for the lock file).
178
200
179
201
202
+ ``extras ``
203
+ ==========
204
+
205
+ - **Type **: Array of strings
206
+ - **Required? **: no; defaults to ``[] ``
207
+ - **Inspiration **: :ref: `packaging:pyproject-tool-table `
208
+ - The list of :ref: `extras <packaging:core-metadata-provides-extra >` supported
209
+ by this lock file.
210
+ - Lockers MAY choose to not support writing lock files that support extras and
211
+ dependency groups (i.e. tools may only support exporting a single-use lock
212
+ file).
213
+ - Tools supporting extras MUST also support dependency groups.
214
+ - Tools should explicitly set this key to an empty array to signal that the
215
+ inputs used to generate the lock file had no extras (e.g. a ``pyproject.toml ``
216
+ file had no ``[project.optional-dependencies] `` table), signalling that the
217
+ lock file is, in effect, multi-use even if it only looks to be single-use.
218
+
219
+
220
+ ``dependency-groups ``
221
+ =====================
222
+
223
+ - **Type **: Array of strings
224
+ - **Required? **: no; defaults to ``[] ``
225
+ - **Inspiration **: :ref: `packaging:pyproject-tool-table `
226
+ - The list of :ref: `packaging:dependency-groups ` supported by this lock file.
227
+ - Lockers MAY choose to not support writing lock files that support extras and
228
+ dependency groups (i.e. tools may only support exporting a single-use lock
229
+ file).
230
+ - Tools supporting dependency groups MUST also support extras.
231
+ - Tools SHOULD explicitly set this key to an empty array to signal that the
232
+ inputs used to generate the lock file had no extras (e.g. a ``pyproject.toml ``
233
+ file had no ``[dependency-groups] `` table), signalling that the lock file
234
+ is, in effect, multi-use even if it only looks to be single-use.
235
+
236
+
180
237
``created-by ``
181
238
==============
182
239
@@ -269,18 +326,6 @@ consistent order. Usage of inline tables SHOULD also be kept consistent.
269
326
informational for auditing purposes.
270
327
271
328
272
- .. Installation
273
-
274
- ``packages.direct ``
275
- -------------------
276
-
277
- - **Type **: boolean
278
- - **Required? **: no; defaults to ``false ``
279
- - **Inspiration **: :ref: `packaging:direct-url `
280
- - Represents whether the installation is via a
281
- :ref: `direct URL reference <packaging:direct-url >`.
282
-
283
-
284
329
.. Source
285
330
286
331
``[packages.vcs] ``
@@ -297,6 +342,8 @@ consistent order. Usage of inline tables SHOULD also be kept consistent.
297
342
and/or installation perspective.
298
343
- Tools SHOULD provide a way for users to opt in/out of using version control
299
344
systems.
345
+ - Installation from a version control system is considered originating from a
346
+ :ref: `direct URL reference <packaging:direct-url >`.
300
347
301
348
302
349
``packages.vcs.type ``
@@ -380,6 +427,8 @@ consistent order. Usage of inline tables SHOULD also be kept consistent.
380
427
- Tools MAY choose to not support local directories, both from a locking
381
428
and/or installation perspective.
382
429
- Tools SHOULD provide a way for users to opt in/out of using local directories.
430
+ - Installation from a directory is considered originating from a
431
+ :ref: `direct URL reference <packaging:direct-url >`.
383
432
384
433
385
434
``packages.directory.path ``
@@ -415,11 +464,14 @@ See ``packages.vcs.subdirectory``.
415
464
- **Type **: table
416
465
- **Required? **: no
417
466
- **Inspiration **: :ref: `packaging:direct-url-data-structure-archive `
418
- - An archive file containing a
419
- :ref: `packaging:source-distribution-format-source-tree `.
467
+ - A direct reference to an archive file to install from
468
+ (this can include wheels and sdists, as well as other archive formats
469
+ containing a source tree).
420
470
- Tools MAY choose to not support archive files, both from a locking
421
471
and/or installation perspective.
422
472
- Tools SHOULD provide a way for users to opt in/out of using archive files.
473
+ - Installation from an archive file is considered originating from a
474
+ :ref: `direct URL reference <packaging:direct-url >`.
423
475
424
476
425
477
``packages.archive.url ``
@@ -445,6 +497,16 @@ See ``packages.vcs.path``.
445
497
size is available via the Content-Length _ header from a HEAD _ HTTP request).
446
498
447
499
500
+ ``packages.archive.upload-time ``
501
+ ''''''''''''''''''''''''''''''''
502
+
503
+ - **Type **: datetime
504
+ - **Required? **: no
505
+ - **Inspiration **: :ref: `packaging:simple-repository-api `
506
+ - The time the file was uploaded.
507
+ - The date and time MUST be recorded in UTC.
508
+
509
+
448
510
``[packages.archive.hashes] ``
449
511
''''''''''''''''''''''''''''''
450
512
@@ -507,11 +569,7 @@ See ``packages.vcs.subdirectory``.
507
569
``packages.sdist.upload-time ``
508
570
''''''''''''''''''''''''''''''
509
571
510
- - **Type **: datetime
511
- - **Required? **: no
512
- - **Inspiration **: :ref: `packaging:simple-repository-api `
513
- - The time the file was uploaded.
514
- - The date and time MUST be recorded in UTC.
572
+ See ``packages.archive.upload-time ``.
515
573
516
574
517
575
``packages.sdist.url ``
@@ -564,7 +622,7 @@ See ``packages.archive.hashes``.
564
622
``packages.wheels.upload-time ``
565
623
'''''''''''''''''''''''''''''''
566
624
567
- See ``packages.sdist .upload-time ``.
625
+ See ``packages.archive .upload-time ``.
568
626
569
627
570
628
``packages.wheels.url ``
@@ -638,6 +696,63 @@ See ``packages.archive.hashes``.
638
696
- See ``packages.tool ``.
639
697
640
698
699
+ -------------------------------------
700
+ Additions to marker expression syntax
701
+ -------------------------------------
702
+
703
+ This PEP proposes adding to the
704
+ :ref: `packaging:dependency-specifiers-environment-markers ` specification such
705
+ that extras and dependency group reliationships for an entry in ``[[packages]] ``
706
+ can be expressed in ``packages.marker ``. The additions outlined in this PEP
707
+ ONLY apply in the context of lock files as defined by this PEP and not in other
708
+ contexts where marker syntax is used (e.g. ``METADATA ``, ``pyproject.toml ``).
709
+
710
+ First, two new markers will be introduced: ``extras `` and
711
+ ``dependency_groups ``. They represent the extras and dependency groups that have
712
+ been requested to be installed, respectively.
713
+
714
+ Second, the marker specification will be changed to allow for containers and not
715
+ just strings for values. ONLY the new markers introduced in this PEP MAY and
716
+ MUST must be used with a :py:class: `collections.abc.Container ` type for their
717
+ value (which default to empty containers). An assumption of the type of
718
+ container used MUST NOT be made (e.g. could be a set, list, or tuple).
719
+
720
+ Third, the "<marker_op> operators that are not in <version_cmp>" will be changed
721
+ from operating "the same as they do for *strings *" to "the same as they
722
+ do for *containers *". There are no backwards-compatibility concerns as strings
723
+ are containers themselves.
724
+
725
+ Fourth, a tool MUST raise an error if an extra or dependency group is specified
726
+ in a marker expression that does not exist in ``packages.extras `` and
727
+ ``packages.dependency-groups ``, respectively.
728
+
729
+ These changes, along with ``packages.extras ``/ ``packages.dependency-groups ``
730
+ and marker expressions' Boolean logic support, allow for expressing arbitrary,
731
+ exhaustive requirements for when a package should be installed based on the
732
+ extras and dependency groups (not) requested. For example, if a lock file has
733
+ ``extras = ["extra-1", "extra-2"] ``, you can specify if a package should be
734
+ installed when:
735
+
736
+ - Any of the extras are specified
737
+ (``'extra-1' in extras or 'extra-2' in extras ``)
738
+ - Only "extra-1" is specified
739
+ (``'extra-1' in extras and 'extra-2' not in extras ``)
740
+ - None of the extras are specified
741
+ (``'extra-1' not in extras and 'extra-2' not in extras ``)
742
+
743
+ (This list is not exhaustive of all possible Boolean logic expressions.)
744
+
745
+ The same flexibility applies to dependency groups.
746
+
747
+ How users tell a tool what extras and/or dependency groups they want installed
748
+ is up to the tool. Tools MUST default to no extras or dependency groups being
749
+ requested by the user if no extras or dependency groups are requested.
750
+ Installers MUST support the marker expression syntax additions as
751
+ proposed by this PEP. Lockers MAY support writing lock files that utilize the
752
+ proposed marker expression syntax additions (i.e. lockers can choose to only
753
+ support writing single-use lock files).
754
+
755
+
641
756
-------
642
757
Example
643
758
-------
@@ -707,10 +822,10 @@ a suggestion):
707
822
skip to the next package.
708
823
#. If ``requires-python `` is specified, check if it is satisfied; an error
709
824
MUST be raised if it isn't.
710
- #. Check that no other instance of the package has been slated to be
711
- installed; an error about the ambiguity MUST be raised otherwise.
825
+ #. Check that no other conflicting instance of the package has been slated to
826
+ be installed; an error about the ambiguity MUST be raised otherwise.
712
827
#. Check that the source of the package is specified appropriately (i.e.
713
- there are not conflicting sources in the package entry);
828
+ there are no conflicting sources in the package entry);
714
829
an error MUST be raised if any issues are found.
715
830
#. Add the package to the set of packages to install.
716
831
@@ -759,6 +874,7 @@ a suggestion):
759
874
- If ``url `` is set, try to use it; tools MAY use
760
875
``packages.index `` or some tool-specific mechanism to download the
761
876
file.
877
+
762
878
#. Validate the file size and hash.
763
879
#. Build the package.
764
880
#. Install.
@@ -795,6 +911,8 @@ from the URL or path is also to help make reading the list of wheel files easier
795
911
as it encodes information that can be useful when understanding and auditing a
796
912
file. Recording the sdist file name is for the same reason.
797
913
914
+ This PEP supports multi-use lock files while requirements files are single-use.
915
+
798
916
799
917
=======================
800
918
Backwards Compatibility
@@ -855,6 +973,20 @@ installed and not a malicious one that someone may have slipped in. It also
855
973
lets one be more deliberate in upgrading their dependencies and thus making sure
856
974
the change is on purpose and not one slipped in by a bad actor.
857
975
976
+ Lock files can support only certain *environments *. What must be installed for
977
+ the environment they are installing for may be different compared to another,
978
+ different environment. Some lock files do try to be *universal *, though, and
979
+ work for any possible environment (sdist and source tree installation success
980
+ permitting).
981
+
982
+ Lock files can be *single-use * or *multi-use *. Single-use lock files are for
983
+ single use-cases. Multi-use lock files can be used for multiple
984
+ use-cases based on extras and dependency groups. It is up to the tool(s) you use
985
+ that decide whether multi-use lock files are possible. All tools dealing with
986
+ lock files at least support single-use lock files. Neither type of lock file
987
+ is better or worse than the other, it just changes how much can be written down
988
+ in a single file (which can influence how manageable).
989
+
858
990
859
991
========================
860
992
Reference Implementation
@@ -1045,30 +1177,21 @@ decide it wasn't worth trying to do in this PEP upfront. Instead, a future PEP
1045
1177
could propose a solution.
1046
1178
1047
1179
1048
- Recording possible extras and dependency groups
1049
- ===============================================
1180
+ A dedicated ``direct `` key
1181
+ ==========================
1182
+
1183
+ Earlier versions had a dedicated ``packages.direct `` key to flag when something
1184
+ should be considered as originating from a
1185
+ :ref: `direct URL reference <packaging:direct-url >`. But there are explicitly
1186
+ only three cases where a direct URL reference can occur (VCS, directory, and
1187
+ archive). Since all three cases are explicit in ``[[packages]] ``, the setting of
1188
+ the key was technically superfluous.
1050
1189
1051
- To expand the feature set of this PEP such that tools could use this PEP for
1052
- their main lock file format, recording possible extras and dependency groups
1053
- that a user may request was considered. The idea was that if you were locking
1054
- from a ``pyproject.toml `` file then you would want to lock for all
1055
- possibilities; that includes extras and dependency groups.
1056
-
1057
- To make this work one would need a way to declare when a package applied to an
1058
- extra and/or dependency group. Due to the possibility that a combination of
1059
- extras and/or dependency groups could change version requirements of a package
1060
- (e.g. extra A needed the spam package at any version, but extra B needed the
1061
- spam package older than version 2, thus making whether extra B was requested
1062
- override what extra A requested), it would require either full Boolean logic
1063
- support or only locking to a specific version of a package no matter what extras
1064
- and/or dependency groups were requested. The former would at least require
1065
- coming up with semantics around a marker for dependency groups, and the latter
1066
- would require a separate lock file any time one didn't want a single version
1067
- restriction.
1068
-
1069
- In the end, there wasn't enough interest from tools for using this PEP as
1070
- their sole lock file format to warrant doing the work to implement this feature
1071
- at this time.
1190
+ The single drawback of not having this key is for wheels and sdists that now
1191
+ fall under ``packages.archive ``. With the separate key (or having it specified
1192
+ as a part of ``packages.sdist `` or ``packages.wheels ``), it would allow for
1193
+ identifying in the lock file itself that an archive file is an sdist of wheel.
1194
+ As it currently stand, an installer must infer that detail itself.
1072
1195
1073
1196
1074
1197
--------------
0 commit comments