Skip to content

Commit 5d27124

Browse files
authored
PEP 751: drop packages.direct and add extras and dependency-groups (#4287)
1 parent 11dd438 commit 5d27124

File tree

1 file changed

+174
-51
lines changed

1 file changed

+174
-51
lines changed

peps/pep-0751.rst

Lines changed: 174 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@ community (PDM_, ``pip freeze``, pip-tools_, Poetry_, and uv_), there seems to
3535
be an appetite for lock files in general.
3636

3737
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
3939
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).
4243

4344
The lack of a standard also has some drawbacks. For instance, any tooling that
4445
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,
4748
etc.). It also impacts portability between tools, which causes vendor lock-in.
4849
By not having compatibility and interoperability it fractures tooling around
4950
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
5152
around auditing a lock file). Rallying around a single format removes this
5253
cost/barrier.
5354

@@ -80,7 +81,9 @@ creating a lock file.
8081

8182
The data in the file should be consumable by tools not written in Python. This
8283
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).
8487

8588
The file format should promote good security defaults. As the format is not
8689
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
9295
this PEP can, at minimum, act as an export target for tools which have their own
9396
internal lock file format.
9497

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+
95116

96117
=============
97118
Specification
@@ -139,6 +160,7 @@ recorded in a consistent order (if inspiration is desired, this PEP has tried to
139160
write down keys in a logical order). As well, tools SHOULD sort arrays in
140161
consistent order. Usage of inline tables SHOULD also be kept consistent.
141162

163+
.. File details
142164
143165
``lock-version``
144166
================
@@ -177,6 +199,41 @@ consistent order. Usage of inline tables SHOULD also be kept consistent.
177199
(i.e. the minimum viable Python version for the lock file).
178200

179201

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+
180237
``created-by``
181238
==============
182239

@@ -269,18 +326,6 @@ consistent order. Usage of inline tables SHOULD also be kept consistent.
269326
informational for auditing purposes.
270327

271328

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-
284329
.. Source
285330
286331
``[packages.vcs]``
@@ -297,6 +342,8 @@ consistent order. Usage of inline tables SHOULD also be kept consistent.
297342
and/or installation perspective.
298343
- Tools SHOULD provide a way for users to opt in/out of using version control
299344
systems.
345+
- Installation from a version control system is considered originating from a
346+
:ref:`direct URL reference <packaging:direct-url>`.
300347

301348

302349
``packages.vcs.type``
@@ -380,6 +427,8 @@ consistent order. Usage of inline tables SHOULD also be kept consistent.
380427
- Tools MAY choose to not support local directories, both from a locking
381428
and/or installation perspective.
382429
- 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>`.
383432

384433

385434
``packages.directory.path``
@@ -415,11 +464,14 @@ See ``packages.vcs.subdirectory``.
415464
- **Type**: table
416465
- **Required?**: no
417466
- **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).
420470
- Tools MAY choose to not support archive files, both from a locking
421471
and/or installation perspective.
422472
- 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>`.
423475

424476

425477
``packages.archive.url``
@@ -445,6 +497,16 @@ See ``packages.vcs.path``.
445497
size is available via the Content-Length_ header from a HEAD_ HTTP request).
446498

447499

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+
448510
``[packages.archive.hashes]``
449511
''''''''''''''''''''''''''''''
450512

@@ -507,11 +569,7 @@ See ``packages.vcs.subdirectory``.
507569
``packages.sdist.upload-time``
508570
''''''''''''''''''''''''''''''
509571

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``.
515573

516574

517575
``packages.sdist.url``
@@ -564,7 +622,7 @@ See ``packages.archive.hashes``.
564622
``packages.wheels.upload-time``
565623
'''''''''''''''''''''''''''''''
566624

567-
See ``packages.sdist.upload-time``.
625+
See ``packages.archive.upload-time``.
568626

569627

570628
``packages.wheels.url``
@@ -638,6 +696,63 @@ See ``packages.archive.hashes``.
638696
- See ``packages.tool``.
639697

640698

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+
641756
-------
642757
Example
643758
-------
@@ -707,10 +822,10 @@ a suggestion):
707822
skip to the next package.
708823
#. If ``requires-python`` is specified, check if it is satisfied; an error
709824
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.
712827
#. 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);
714829
an error MUST be raised if any issues are found.
715830
#. Add the package to the set of packages to install.
716831

@@ -759,6 +874,7 @@ a suggestion):
759874
- If ``url`` is set, try to use it; tools MAY use
760875
``packages.index`` or some tool-specific mechanism to download the
761876
file.
877+
762878
#. Validate the file size and hash.
763879
#. Build the package.
764880
#. Install.
@@ -795,6 +911,8 @@ from the URL or path is also to help make reading the list of wheel files easier
795911
as it encodes information that can be useful when understanding and auditing a
796912
file. Recording the sdist file name is for the same reason.
797913

914+
This PEP supports multi-use lock files while requirements files are single-use.
915+
798916

799917
=======================
800918
Backwards Compatibility
@@ -855,6 +973,20 @@ installed and not a malicious one that someone may have slipped in. It also
855973
lets one be more deliberate in upgrading their dependencies and thus making sure
856974
the change is on purpose and not one slipped in by a bad actor.
857975

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+
858990

859991
========================
860992
Reference Implementation
@@ -1045,30 +1177,21 @@ decide it wasn't worth trying to do in this PEP upfront. Instead, a future PEP
10451177
could propose a solution.
10461178

10471179

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.
10501189

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.
10721195

10731196

10741197
--------------

0 commit comments

Comments
 (0)