@@ -256,7 +256,7 @@ Default priorities
256256
257257The ``default-priorities `` dictionary defines the ordering of
258258namespaces which is used in variant ordering. The exact algorithm is
259- described in the `Variant ordering `_ section.
259+ described in the `Variant ordering and selection `_ section.
260260
261261The following key is REQUIRED:
262262
@@ -462,11 +462,97 @@ consistency`_ and set out in full in
462462:ref: `pep825-metadata-consistency `.
463463
464464
465- Variant ordering
466- ----------------
465+ Variant ordering and selection
466+ ------------------------------
467+
468+ High-level overview
469+ '''''''''''''''''''
470+
471+ This specification defines an ordering of wheels based on their variant
472+ metadata, from the most preferable to the least preferable.
473+
474+ The ordering of wheels by platform compatibility tags is not currently
475+ defined by the specification, beyond a guideline that more specific
476+ wheels should be preferred. This has not been a big problem, as usually
477+ there is only one wheel that is compatible with the system, and where
478+ there are more, the ordering is either clear or insignificant.
479+
480+ With wheel variants, there can be several compatible wheels per project
481+ version. We define a total ordering to give package authors and users
482+ control over wheel preference and to ensure that all tools select the
483+ same variant without ambiguity. Tools may allow users to override this
484+ ordering.
485+
486+ Every variant property is a ``namespace :: feature :: value `` triple
487+ whose components are ranked in that order: by namespace first, then by
488+ feature within the namespace, then by value within the feature. Only
489+ properties compatible with the target system take part, and only the highest
490+ ranking compatible value for each feature. The ranking for namespaces comes
491+ from the package's variant metadata. The ranking for their features and the
492+ ranking of values within features will be defined in a subsequent PEP.
493+
494+ Variant wheels sort as Python sorts lists of tuples, each wheel being
495+ the list of its property triples ordered best first. Where one wheel's
496+ triples run out, the longer list therefore wins. The intuition is that
497+ this selects the wheel that makes the best use of the target hardware.
498+
499+ Spelled out: compare two wheels by their best-ranked property triple; if
500+ those are equal, use the next lower-ranked property as a tiebreaker,
501+ repeatedly until an ordering is established. Comparing all compatible
502+ wheels this way yields the most preferred wheel.
503+
504+ As an example, take four variant wheels of a package that ranks the
505+ ``nvidia `` namespace above ``x86_64 ``:
506+
507+ .. code :: python
467508
468- This specification defines an ordering between different wheels based on
469- the presence of variant metadata.
509+ # Rankings, most preferred first. The namespace ranking comes from
510+ # the package's variant metadata; the feature and value rankings
511+ # will be defined in a subsequent PEP.
512+ namespaces = [" nvidia" , " x86_64" ]
513+ features = {" nvidia" : [" cuda_version_lower_bound" ], " x86_64" : [" level" ]}
514+ values = {
515+ " nvidia" : {" cuda_version_lower_bound" : [" 13.0" , " 12.0" ]},
516+ " x86_64" : {" level" : [" v4" , " v3" , " v2" ]},
517+ }
518+
519+ # Each variant label maps to its properties, with only the best
520+ # compatible value kept for every feature.
521+ variant_wheels = {
522+ " gpu" : [(" nvidia" , " cuda_version_lower_bound" , " 13.0" )],
523+ " gpu_cpuv2" : [(" nvidia" , " cuda_version_lower_bound" , " 13.0" ),
524+ (" x86_64" , " level" , " v2" )],
525+ " gpu_cpuv4" : [(" nvidia" , " cuda_version_lower_bound" , " 13.0" ),
526+ (" x86_64" , " level" , " v4" )],
527+ " cpuv4" : [(" x86_64" , " level" , " v4" )],
528+ }
529+
530+ def rank (prop ):
531+ """ Rank a triple. The ranking lists put the best first, so negate
532+ the positions to make a bigger rank mean a better property."""
533+ namespace, feature, value = prop
534+ return (- namespaces.index(namespace),
535+ - features[namespace].index(feature),
536+ - values[namespace][feature].index(value))
537+
538+ def sort_key (label ):
539+ """ Rank a wheel: its property ranks, best first."""
540+ return sorted (map (rank, variant_wheels[label]), reverse = True )
541+
542+ sorted (variant_wheels, key = sort_key, reverse = True )
543+ # ['gpu_cpuv4', 'gpu_cpuv2', 'gpu', 'cpuv4']
544+
545+ The ``gpu* `` wheels sort ahead of ``cpuv4 `` because their best triple
546+ is in the higher-ranked ``nvidia `` namespace. ``gpu_cpuv4 `` beats
547+ ``gpu_cpuv2 `` on their second triple, since ``v4 `` outranks ``v2 ``. Both
548+ beat ``gpu ``, whose triples run out first, as the wheel with more
549+ properties wins.
550+
551+ The ordering for non-variant wheels remains unchanged.
552+
553+
554+ Ordering algorithm
555+ ''''''''''''''''''
470556
471557For the purpose of ordering, the combined variant metadata for all
472558candidate variant wheels MUST be obtained. It can be sourced either from
@@ -481,12 +567,12 @@ features, and features into namespaces. For every namespace, the tool
481567MUST obtain a list of compatible features, and for every feature, a list
482568of compatible values. The method of obtaining these lists will be
483569defined in a subsequent PEP. The items in these lists will be provided
484- in specific order that will impact variant wheel ordering .
570+ ordered from the most preferable to the least preferable .
485571
486572The compatible wheels corresponding to a particular combination of
487- package name, version and build number MUST be grouped by their variant
488- label, and a separate group of non-variant wheels MUST be formed. The
489- groups of variant wheels MUST then be ordered according to the following
573+ package name and version MUST be grouped by their variant label, and a
574+ separate group of non-variant wheels MUST be formed. The groups of
575+ variant wheels MUST then be ordered according to the following
490576algorithm:
491577
4925781. Construct the ordered list of namespaces by copying the value of the
@@ -502,7 +588,7 @@ algorithm:
5025884. For every group, determine the most preferred value corresponding to
503589 every variant feature present in the variant properties corresponding
504590 to the group. This is done by finding among the values the one that
505- has the lowest position in the ordered property value list. After
591+ has the lowest index in the ordered property value list. After
506592 this step, a list of features along with their best values is
507593 available for every variant. This is done in the
508594 ``VariantWheel.best_value_properties() `` method in the example.
@@ -514,7 +600,9 @@ algorithm:
514600 function in the example.
515601
5166026. For every group, sort the list constructed in step 4 using the sort
517- keys constructed in step 5, in ascending order. This is done by the
603+ keys constructed in step 5, in ascending order. The resulting list
604+ will be ordered from the most preferable feature to the least
605+ preferable feature. This is done by the
518606 ``VariantWheel.sorted_properties() `` method in the example.
519607
5206087. To order groups, compare their sorted lists from step 6. If the
@@ -524,17 +612,21 @@ algorithm:
524612 found or the list in one of the groups is exhausted. In the latter
525613 case, the group with more keys is sorted earlier. As a fallback,
526614 if both groups have the same number of keys, they are ordered
527- lexically by the variant label, ascending. This is done by the
528- ultimate step of the example algorithm, with the comparison function
529- being implemented as ``VariantWheel.__lt__() ``.
615+ lexically by the variant label, ascending. The resulting list of
616+ groups will be sorted from the most preferable to the least
617+ preferable. This is done by the ultimate step of the example
618+ algorithm, with the comparison function being implemented as
619+ ``VariantWheel.__lt__() ``.
530620
531621The algorithm sorts the group of null variant wheels last, as they
532622feature no variant properties. The group of non-variant wheels MUST be
533623placed after all the other groups.
534624
535625Within every group, the wheels MUST then be ordered according to their
536- platform compatibility tags. After this process, the variant wheels are
537- sorted from the most preferred to the least preferred.
626+ remaining properties, such as platform compatibility tags and build
627+ numbers. This specification does not alter this ordering; it remains the
628+ same as before. After completing this process, the wheels are sorted
629+ from the most preferred to the least preferred.
538630
539631The tools MAY provide options to override the default ordering, for
540632example by specifying a preference for specific namespaces, features
@@ -696,7 +788,7 @@ The variant markers MUST only be used in dependency specifiers and MUST
696788NOT take part in selecting a wheel. These markers gate the individual
697789dependency specifiers of a wheel that has already been selected. They
698790MUST be evaluated only once variant wheel selection, as described in
699- `variant ordering `_, has taken place.
791+ `variant ordering and selection `_, has taken place.
700792
701793Their values MUST be determined as follows:
702794
@@ -883,9 +975,9 @@ behavior would be to:
8839757. Obtain the ordered lists of compatible variant properties. The
884976 mechanism for this will be specified in a subsequent PEP.
8859778. Filter and order variants based on the lists of compatible
886- properties, per ` variant ordering `_, and select the most preferred
887- variant . If no variant wheel matched, use the non-variant wheels by
888- their rules.
978+ properties and select the most preferred variant, per ` variant
979+ ordering and selection `_ . If no variant wheel matched, use the
980+ non-variant wheels by their rules.
8899819. If multiple wheels for a given version share the same variant label,
890982 order them by Platform compatibility tags and build number, and
891983 select the best wheel.
@@ -1034,8 +1126,8 @@ When a package manager is requested to install ``torch``, in order:
10341126 the list.
10351127
103611285. If variant wheels with multiple different labels remain on the list,
1037- the results are sorted per the algorithm in `variant ordering `_.
1038- The most preferred label is selected.
1129+ the results are sorted per the algorithm in `variant ordering and
1130+ selection `_. The most preferred label is selected.
10391131
10401132 For example, in this case the ``cuda* `` wheels are ordered by their
10411133 properties, using the lists obtained in step 4. The wheels for CUDA
@@ -1103,9 +1195,9 @@ sources.
11031195
11041196A tool that searches sources in priority order (for example, the "index
11051197priority" in :pep: `766 `) can order variants from one source at a time
1106- using the `variant ordering `_ algorithm, and proceed to the next source
1107- only if the current one has no viable candidates. No cross-source
1108- metadata merge is necessary.
1198+ using the `variant ordering and selection `_ algorithm, and proceed to
1199+ the next source only if the current one has no viable candidates. No
1200+ cross-source metadata merge is necessary.
11091201
11101202A tool that collates candidates from all sources before selecting among
11111203them (for example, "version priority" in :pep: `766 `) must compare wheels
@@ -1264,12 +1356,12 @@ and a publisher of variant wheels may still take that route.
12641356The requirement is what makes several of the mechanisms defined here
12651357work at all. If the wheels of one release disagreed about namespace
12661358order, there would be no total order over the variants, so `variant
1267- ordering `_ would have no defined output and two conforming installers
1268- could select different wheels from the same inputs. `` pylock.toml ``
1269- inlines combined variant metadata, so non-deterministic combination
1270- would mean that two lock runs over one release can produce different
1271- lock files. And the `index-level metadata `_ file could neither be
1272- generated from the uploaded wheels alone, nor be relied upon once
1359+ ordering and selection `_ would have no defined output and two conforming
1360+ installers could select different wheels from the same inputs.
1361+ `` pylock.toml `` inlines combined variant metadata, so non-deterministic
1362+ combination would mean that two lock runs over one release can produce
1363+ different lock files. And the `index-level metadata `_ file could neither
1364+ be generated from the uploaded wheels alone, nor be relied upon once
12731365generated, since a resolver would then have to fetch every candidate
12741366wheel to discover the true combined picture.
12751367
@@ -1319,7 +1411,7 @@ only ``120_real``, much as a dependency that supports a single CPU
13191411architecture is gated on ``platform_machine ``. Where a dependency does
13201412exist for every value of a feature, it can instead be published as a
13211413variant package and depended upon unconditionally, leaving the choice to
1322- `variant ordering `_.
1414+ `variant ordering and selection `_.
13231415
13241416Because of this filtering, the variant markers do not depart from the
13251417established meaning of an environment marker. Every property in
@@ -1342,10 +1434,10 @@ Three consequences of this design are worth noting:
13421434 for every feature it declares (see `variant properties `_).
13431435 ``variant_features `` and ``variant_namespaces `` therefore always list
13441436 every feature and namespace the wheel was built for. This is why the
1345- overrides permitted in `variant ordering `_ may not reach past the
1346- compatibility filter: a wheel selected in spite of being unsupported
1347- could have its properties filtered away, and the dependencies gated on
1348- them would silently disappear.
1437+ overrides permitted in `variant ordering and selection `_ may not reach
1438+ past the compatibility filter: a wheel selected in spite of being
1439+ unsupported could have its properties filtered away, and the
1440+ dependencies gated on them would silently disappear.
13491441
13501442- For the null variant, ``variant_label `` is ``"null" `` and the three
13511443 set-valued markers are empty sets, as the null variant has zero
@@ -1610,6 +1702,13 @@ Change History
16101702 The investigation in the appendix settles the question in favour of
16111703 markers, so the Open Issues section has been removed and the
16121704 reasoning recorded in the Rationale instead.
1705+ - Removed grouping by build numbers, instead relegating them to
1706+ tie-breaking along with other wheel properties. This reverts a
1707+ potential change in behavior for non-variant wheels.
1708+ - Added a high-level overview to `variant ordering and selection `_,
1709+ clarified that the ordering of the lists used in the algorithm is
1710+ from the most preferable to the least preferable and that the
1711+ ordering by platform compatibility tags is not changed.
16131712
16141713- 19-Aug-2026
16151714
0 commit comments