@@ -308,11 +308,17 @@ there MUST exist a corresponding ``{name}-{version}-variants.json``
308308file. The ``{name} `` and ``{version} `` placeholders correspond to the
309309package name and version, normalized according to the same rules as
310310wheel files, as found in the :ref: `packaging:wheel-file-name-spec ` of
311- the Binary Distribution Format specification. The exact URL where the
312- file is hosted is insignificant, but a link to it MUST be present on all
313- index pages where the variant wheels are linked. It is presented in the
314- same simple repository format as source distribution and wheel links in
315- the index, including an (OPTIONAL) hash.
311+ the Binary Distribution Format specification.
312+
313+ The exact URL where the file is hosted is insignificant, but it MUST
314+ be provided in all the responses where the variant wheels are included.
315+ It should follow the rules for files in the
316+ :ref: `packaging:simple-repository-api `, except that the optional
317+ metadata attributes served by the index (such as ``core-metadata ``,
318+ ``dist-info-metadata ``, ``requires-python `` or ``yanked ``) are not
319+ meaningful for that file. Indexes MAY publish or skip these attributes,
320+ as long as the values do not prevent correct operation. Tools MAY either
321+ use or ignore these values.
316322
317323This file uses the same structure as `variant metadata `_, except that
318324the ``variants `` object MUST list all variants available on the package
@@ -379,17 +385,20 @@ like:
379385 Variant ordering
380386----------------
381387
382- To determine which variant wheel to install when multiple wheels are
383- compatible, variants MUST be totally ordered by their variant
384- properties.
388+ This specification defines an ordering between different wheels based on
389+ the presence of variant metadata.
385390
386391For the purpose of ordering, variant properties are grouped into
387392features, and features into namespaces. For every namespace, the tool
388- MUST obtain an ordered list of compatible features, and for every
389- feature, an ordered list of compatible values. The method of obtaining
390- these lists will be defined in a subsequent PEP.
391-
392- The default ordering MUST be performed equivalent to the following
393+ MUST obtain a list of compatible features, and for every feature, a list
394+ of compatible values. The method of obtaining these lists will be
395+ defined in a subsequent PEP. The items in these lists will be provided
396+ in specific order that will impact variant wheel ordering.
397+
398+ The compatible wheels corresponding to a particular combination of
399+ package name, version and build number MUST be grouped by their variant
400+ label, and a separate group of non-variant wheels MUST be formed. The
401+ groups of variant wheels MUST then be ordered according to the following
393402algorithm:
394403
3954041. Construct the ordered list of namespaces by copying the value of the
@@ -402,9 +411,10 @@ algorithm:
402411 value of the respective ``default-priorities.feature.{namespace} ``
403412 key.
404413
405- ii. Obtain the compatible feature names, in order. For every feature
406- name that is not present in the constructed list, append it to
407- the end.
414+ ii. Take the ordered list of compatible feature names obtained
415+ previously and iterate over it, in order. For every feature name
416+ that is not present in the constructed list, append it to the
417+ end.
408418
409419 After this step, a list of ordered feature names is available for
410420 every namespace. This is ``feature_order `` in the example.
@@ -415,48 +425,49 @@ algorithm:
415425 of the respective
416426 ``default-priorities.property.{namespace}.{feature_name} `` key.
417427
418- ii. Obtain the compatible feature values, in order. For every value
419- that is not present in the constructed list, append it to the
420- end.
428+ ii. Take the ordered list of compatible feature values obtained
429+ previously and iterate over it, in order. For every value that is
430+ not present in the constructed list, append it to the end.
421431
422432 After this step, a list of ordered property values is available for
423433 every feature. This is ``value_order `` in the example.
424434
425- 4. For every compatible variant , determine the most preferred value
426- corresponding to every feature in that variant. This is done by
427- finding among the values present in the variant properties the one
428- that has the lowest position in the ordered property value list.
429- After this step, a list of features along with their best values
430- is available for every variant. This is done in the
431- ``Variant .best_value_properties() `` method in the example.
435+ 4. For every group , determine the most preferred value corresponding to
436+ every variant feature present in the variant properties corresponding
437+ to the group. This is done by finding among the values the one that
438+ has the lowest position in the ordered property value list. After
439+ this step, a list of features along with their best values is
440+ available for every variant. This is done in the
441+ ``VariantWheel .best_value_properties() `` method in the example.
432442
4334435. For every item in the list constructed in the previous step,
434444 construct a sort key that is a 3-tuple consisting of
435445 its namespace, feature name and best feature value indices in the
436446 respective ordered lists. This is done by the ``property_key() ``
437447 function in the example.
438448
439- 6. For every compatible variant , sort the list constructed in step 4
440- using the sort keys constructed in step 5, in ascending order. This
441- is done by the `` Variant .sorted_properties() `` method in the example.
449+ 6. For every group , sort the list constructed in step 4 using the sort
450+ keys constructed in step 5, in ascending order. This is done by the
451+ `` VariantWheel .sorted_properties() `` method in the example.
442452
443- 7. To order variants , compare their sorted lists from step 6. If the
444- sort keys at the first position are different, the variant with the
453+ 7. To order groups , compare their sorted lists from step 6. If the
454+ sort keys at the first position are different, the group with the
445455 lower key is sorted earlier. If they are the same, compare the keys
446456 at the second position, and so on, until either a tie-breaker is
447- found or the list in one of the variants is exhausted. In the latter
448- case, the variant with more keys is sorted earlier. As a fallback,
449- if both variants have the same number of keys, they are ordered
450- lexically by their variant label, ascending. This is done by the
457+ found or the list in one of the groups is exhausted. In the latter
458+ case, the group with more keys is sorted earlier. As a fallback,
459+ if both groups have the same number of keys, they are ordered
460+ lexically by the variant label, ascending. This is done by the
451461 ultimate step of the example algorithm, with the comparison function
452- being implemented as ``Variant .__lt__() ``.
462+ being implemented as ``VariantWheel .__lt__() ``.
453463
454- After this process, the variant wheels are sorted from the most
455- preferred to the least preferred. The algorithm sorts the null variant
456- after all the other variants. The non-variant wheel MUST be ordered
457- after the null variant. Multiple wheels with the same variant property
458- set (and multiple non-variant wheels) MUST then be ordered according to
459- their platform compatibility tags.
464+ The algorithm sorts the group of null variant wheels last, as they
465+ feature no variant properties. The group of non-variant wheels MUST be
466+ placed after all the other groups.
467+
468+ Within every group, the wheels MUST then be ordered according to their
469+ platform compatibility tags. After this process, the variant wheels are
470+ sorted from the most preferred to the least preferred.
460471
461472The tools MAY provide options to override the default ordering, for
462473example by specifying a preference for specific namespaces, features
@@ -465,7 +476,8 @@ variants, or to select a particular variant.
465476
466477Alternatively, the sort algorithm for variant wheels could be described
467478using the following pseudocode. For simplicity, this code does not
468- account for non-variant wheels or tags.
479+ account for non-variant wheels or the subsequent ordering by platform
480+ compatibility tags.
469481
470482.. code :: python
471483
@@ -530,7 +542,7 @@ account for non-variant wheels or tags.
530542 )
531543
532544
533- class Variant :
545+ class VariantWheel :
534546 """ Example class exposing properties of a variant wheel"""
535547
536548 label: str
@@ -571,13 +583,13 @@ account for non-variant wheels or tags.
571583 return self .label < other.label
572584
573585
574- # A list of variants to sort.
575- variants : list[Variant ] = [... ]
586+ # A list of variant wheels to sort.
587+ variant_wheels : list[VariantWheel ] = [... ]
576588
577589
578- # 7. Order variants by comparing their sorted properties
579- # (see Variant .__lt__())
580- variants .sort()
590+ # 7. Order variant wheels by comparing their sorted properties
591+ # (see VariantWheel .__lt__())
592+ variant_wheels .sort()
581593
582594
583595 Environment markers
@@ -737,6 +749,10 @@ Note that steps 4. through 8. are introduced specifically for variant
737749wheels. The remaining steps correspond to the current installer
738750behavior.
739751
752+ When installing from a source that does not provide an `index-level
753+ metadata `_, the same algorithm can be used, except that the variant
754+ metadata needs to be read directly from the wheels.
755+
740756
741757Installing a local wheel
742758''''''''''''''''''''''''
@@ -786,28 +802,68 @@ To generate the ``{name}-{version}-variants.json`` file:
786802Rationale
787803=========
788804
805+ This PEP is part of a larger variant wheel design that was originally
806+ proposed as :pep: `817 `. However, due to its complexity, we decided to
807+ split it into smaller parts that build one upon another. This PEP is the
808+ first in the series, providing foundations including the file format
809+ along with necessary metadata, index support and basic tool algorithms.
810+ Aspects such as providing actual variant properties or building wheels
811+ are deferred into subsequent PEPs.
812+
789813Variant wheels use structured `variant properties `_ to express
790- multidimensional wheel compatibility matrices. For example, it permits
791- expressing that a single variant requires certain CPU and GPU features
792- independently. It can express both AND-style dependencies (such as
793- different CPU instruction sets) and OR-style dependencies (such as
794- different GPUs supported by a single package).
814+ multidimensional wheel compatibility matrices. Properties are organized
815+ in namespaces that can be defined and governed independently. The
816+ key-value structure makes the properties more flexible: adding a new
817+ compatibility axis can be done by adding a new key. It can support both
818+ AND-style dependencies (for example, a CPU plugin could define multiple
819+ keys corresponding to different instructions sets, all of which are used
820+ in the package and therefore must be supported) and OR-style
821+ dependencies (for example, a GPU plugin can define a single key listing
822+ multiple GPU types, indicating that all of them are supported by the
823+ package, and therefore the users needs to own only one of them).
795824
796825The specification does not impose any formal limits on the number of
797826properties expressed, and specifically accounts for the possibility of
798827property sets being very long (for example, a long list of GPUs or CPU
799- extension sets). To avoid wheel filenames becoming very long, the
800- property lists are stored inside the wheel and mapped to a short label
801- that is intended to be human-readable.
802-
803- To facilitate variant selection while installing from remote index,
804- the variant metadata is mirrored in a JSON file published on the index.
805- This enables installers to obtain variant property mapping without
806- having to fetch individual wheels.
807-
808- Since JSON format does not feature a set type, sets are represented as
809- sorted lists. Sorting ensures that tools can safely use equality
810- comparison over dictionaries.
828+ extension sets). To avoid wheel filenames becoming hard to comprehend
829+ because of excess of information and potentially causing technical
830+ issues because of their length, the property lists are stored inside
831+ the wheel and mapped to a short label that is chosen by the package
832+ maintainer and intended to be human-readable.
833+
834+ Wheel filenames alone do not provide sufficient metadata to drive
835+ variant wheel selection. To avoid tools having to fetch the variant
836+ metadata straight from multiple wheel files, the metadata from wheels
837+ for every package version is combined and republished. This metadata is
838+ scoped to a single package version to permit variants changing in the
839+ future version.
840+
841+ The index support aims to account for three scenarios:
842+
843+ 1. An index implementation that cannot embed additional metadata as part
844+ of file list responses. For example, this covers installing straight
845+ from a directory listing created by a webserver. To account for this
846+ scenario, index-level metadata is published as a plain JSON file that
847+ can be generated by the package maintainer and placed alongside
848+ wheels.
849+
850+ 2. An index implementation that has more complete wheel support but does
851+ not wish to implement full variant wheel support immediately. The
852+ index needs only to permit the user to upload said JSON file. To
853+ account for minimalistic implementation, the specification permits
854+ the index to treat said file similarly to a wheel, including
855+ publishing attributes such as ``yanked ``, as long as their values do
856+ not prevent clients from working.
857+
858+ 3. An index implementation that implements complete wheel variant
859+ support. Such an index will parse uploaded variant wheels, and
860+ dynamically create the index-level metadata. The JSON file path would
861+ then be treated as an API endpoint rather than an actual file.
862+
863+ Since JSON format does not feature a set type, sets in the metadata are
864+ represented as sorted lists. Sorting ensures reproducibility and makes
865+ it possible to use equality comparison over whole dictionaries without
866+ having to convert specific fields back to sets after deserialization.
811867
812868The variant ordering algorithm has been proposed with the assumption
813869that variant properties take precedence over Platform compatibility
@@ -817,15 +873,25 @@ variant may require a different minimal libc version, in which case the
817873selection should be driven by the desired CUDA preference rather than
818874incidental platform tag difference.
819875
820- While the provision of variant properties is deferred to a future PEP to
821- keep the specification easier to comprehend, a baseline assumption is
822- made that the compatible properties will be provided in specific order
823- corresponding to their preference, much like Platform compatibility tags
824- conventionally are. The variant metadata provides the ability to
825- override this order at package level. However, namespaces are unordered
826- by design (e.g. we will not decide upfront which GPU vendors take
827- precedence) and therefore they always need to be ordered by the package
828- maintainer.
876+ While a future PEP will define how variant properties are provided, a
877+ baseline assumption is made that the compatible properties will be
878+ provided in specific order corresponding to their preference. This makes
879+ it possible to use a generic sorting algorithm, and later define
880+ properties as data without having to change the algorithm.
881+
882+ A future PEP will define how the ordering for features and values is
883+ provided. However, namespaces are governed independently and considered
884+ on equal footing, and therefore there will be no standard ordering for
885+ them. Instead, the ordering of namespaces will be explicitly stated in
886+ the variants metadata, which in turn will be provided by the package
887+ maintainer as part of the build process. For completeness, it will also
888+ be possible to provide overrides for the ordering of features and values
889+ via the same mechanism.
890+
891+ In the vast majority of real use cases, ordering based on properties
892+ will suffice. However, in a pathological case two different variant
893+ wheels may end up with equal sort keys. To provide reproducible results
894+ in this case, fallback sorting on variant label is performed.
829895
830896A concept of null variant is introduced that is distinct from
831897non-variant wheels to facilitate a transition period. This variant is
@@ -938,7 +1004,7 @@ Reference Implementation
9381004The `variantlib <https://github.com/wheelnext/variantlib >`__ project
9391005contains a reference implementation of a complete variant wheel
9401006solution. It is compliant with this PEP, but also goes beyond it,
941- providing example solutions to ` open issues `_ .
1007+ providing example solutions to some of the deferred items .
9421008
9431009A client for installing variant wheels is implemented in a
9441010`uv branch <https://github.com/astral-sh/uv/pull/12203 >`__.
@@ -982,15 +1048,36 @@ would be incorrectly deemed compatible because of the
9821048``manylinux_2_27_x86_64 `` part.
9831049
9841050
985- Open Issues
986- ===========
1051+ Replacing Platform compatibility tags entirely
1052+ ----------------------------------------------
1053+
1054+ Technically, it would be entirely possible to convey the information
1055+ currently passed via the Platform compatibility tags via variant
1056+ properties, and remove these explicit tags from the filename. However,
1057+ we decided not to pursue this and instead preserve the existing
1058+ filenames for wheels that do not need additional variants, as we do not
1059+ believe that the effort required to update all the existing workflows
1060+ justifies the benefit of more compact, and slightly more consistent
1061+ naming.
1062+
1063+
1064+ Out of scope
1065+ ------------
9871066
988- The following problems are deferred to subsequent PEPs:
1067+ The following problems are deferred to subsequent PEPs in the series :
9891068
9901069- governance of variant namespaces
9911070- determining which variant properties are compatible with the system
1071+ - overriding the compatibility detection using static data
9921072- building variant wheels
9931073
1074+ In addition to that, the following matters are left
1075+ implementation-defined:
1076+
1077+ - Selecting variant wheels from multiple sources. Currently, there is no
1078+ standard defined behavior for regular wheels, nor consensus across
1079+ different packaging tools on how to handle that.
1080+
9941081
9951082Acknowledgements
9961083================
@@ -1036,6 +1123,12 @@ Change History
10361123 - Changed ``pylock.toml `` integration to inline variant metadata
10371124 rather than storing a URL and a hash.
10381125
1126+ - 11-May-2026
1127+
1128+ - Added replacing platform compatibility tags entirely to rejected
1129+ ideas.
1130+ - Clarified interpretation of sorting algorithm and index support.
1131+
10391132
10401133Appendices
10411134==========
0 commit comments