This PEP proposes variant wheels, an extension to Binary distribution format that permits building multiple variants of the same package while embedding additional compatibility data. The specific properties are stored inside the wheel, and expressed via a human-readable variant label in the filename, which is then mapped to the actual properties via a separately hosted JSON mapping. This aims to make {tool} install {package} capable of selecting the most appropriate variant of packages where additional compatibility dimensions such as GPU support need to be accounted for.
This PEP proposes a protocol to record additional compatibility data in binary packages, to allow tools to pick the correct package to use in situations where Platform compatibility tags are insufficient. There are many cases where this is necessary, most notably in the case of scientific and machine learning (ML) libraries, where high performance requires extension code that is carefully tailored to the precise hardware available in the user’s environment. Well known examples of this include:
The problem space has been explored in greater detail in PEP 817.
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
A variant wheel is an extension of the wheel format, defined in Binary distribution format. It MUST specify a variant label in the filename, which makes it distinct from non-variant wheels. It MUST include a variant metadata file, which maps the variant label to zero or more variant properties.
Variant properties express the compatibility of binary packages with specific platforms, in addition to Platform compatibility tags. They follow a key-value format, where a key is called a variant feature. The keys are further grouped into independently governed variant namespaces. Hence, a variant feature consists of a namespace and a feature name, whereas a variant property consists of a namespace, a feature name and a feature value.
Variant properties are serialized into a structured 3-tuple of the following format:
The properties with which the wheel was built are stored within the wheel, in the variant metadata file. A variant wheel can specify multiple values corresponding to a variant feature. For the wheel to be considered compatible with a system, at least one value for every feature listed in its properties MUST be compatible with the system. A variant wheel with zero properties is always deemed compatible.
The namespace and feature name components MUST be non-empty and consist only of 0-9, a-z and _ ASCII characters (^[a-z0-9_]+$). The feature value MUST be non-empty and consist only of 0-9, a-z, _ and . ASCII Characters (^[a-z0-9_.]+$).
The available properties and the rules governing their compatibility will be defined in a subsequent PEP.
Examples:
The wheel filename template originally defined by PEP 427 is changed to:
The Python tag component MUST NOT start with a digit.
Variant wheels MUST include the variant label component. Conversely, wheels without variant label are non-variant wheels. The variant label MUST be non-empty and consist only of 0-9, a-z, _ and . ASCII characters (^[0-9a-z_.]+$).
Every variant label MUST uniquely correspond to a specific set of variant properties, which MUST be the same for all wheels using the same label within a single package version.
The label null is reserved and always corresponds to the variant with zero properties, called a null variant. This variant acts as a fallback variant that is always compatible.
Examples:
The additional metadata specific to variant wheels is stored inside the wheel, in *.dist-info/variant.json file, using the JSON format. This PEP defines the following structure:
This structure corresponds to the version 0.1.0 of the format. The version number is stored as part of the schema URL. Whenever the format changes, the version number must be incremented. Tools MUST assume that all variant wheels using an unknown format version are unsupported.
The version numbers starting with zero are reserved for drafts and MUST NOT be used in production. Once the proposal is complete, the latest draft will be promoted to version 1.0.0.
The top-level keys are described in the subsequent sections.
The $schema key is the standard way of specifying the JSON schema used. Its value MUST be the URL of a JSON schema corresponding to this specification, hosted on packaging.python.org. The schema URL MUST include a version number, and consequently every schema MUST describe the matching format version. The schema can be used to verify the validity of the JSON file prior to processing it, or after outputting it.
A proposed JSON schema for the current format version is included in the Appendix of this PEP. Subsequent PEPs changing the metadata format will include updated versions of the schema. The schema is available in Appendix: JSON Schema for Variant Metadata.
The default-priorities dictionary defines the ordering of variants. The exact algorithm is described in the Variant ordering section.
The following key is REQUIRED:
It MAY have the following OPTIONAL keys:
The variants dictionary provides a mapping from variant labels to variant properties. In the variant wheel, it MUST contain the label present in that wheel’s filename.
It has 3 levels. The first level keys are variant labels, the second level keys are namespaces, the third level are feature names, and the third level values are sets of feature values, converted to lists, sorted lexically.
For every package version that includes at least one variant wheel, there MUST exist a corresponding {name}-{version}-variants.json file. The {name} and {version} placeholders correspond to the package name and version, normalized according to the same rules as wheel files, as found in the File name convention of the Binary Distribution Format specification.
The exact URL where the file is hosted is insignificant, but it MUST be provided in all the responses where the variant wheels are included. It should follow the rules for files in the Simple repository API, except that the optional metadata attributes served by the index (such as core-metadata, dist-info-metadata, requires-python or yanked) are not meaningful for that file. Indexes MAY publish or skip these attributes, as long as the values do not prevent correct operation. Tools MAY either use or ignore these values.
This file uses the same structure as variant metadata, except that the variants object MUST list all variants available on the package index for the package version in question. The tools MUST ensure that the variant metadata across multiple variant wheels of the same package version and the index-level metadata file is consistent. They MAY require that keys other than variants have exactly the same values, or they may carefully merge their values, provided that no conflicting information is introduced, and the result is the same irrespective of the order in which the wheels are processed.
This file SHOULD NOT be considered immutable and MAY be updated in a backward compatible way at any point (e.g. when adding a new variant).
Variant indexes MAY elect to either auto-generate the file from the uploaded variant wheels or allow the user to manually generate it themselves and upload it to the index.
The foo-1.2.3-variants.json corresponding to the package with two wheel variants, one of them listed in the previous example, would look like:
This specification defines an ordering between different wheels based on the presence of variant metadata.
For the purpose of ordering, variant properties are grouped into features, and features into namespaces. For every namespace, the tool MUST obtain a list of compatible features, and for every feature, a list of compatible values. The method of obtaining these lists will be defined in a subsequent PEP. The items in these lists will be provided in specific order that will impact variant wheel ordering.
The compatible wheels corresponding to a particular combination of package name, version and build number MUST be grouped by their variant label, and a separate group of non-variant wheels MUST be formed. The groups of variant wheels MUST then be ordered according to the following algorithm:
After this step, a list of ordered feature names is available for every namespace. This is feature_order in the example.
After this step, a list of ordered property values is available for every feature. This is value_order in the example.
The algorithm sorts the group of null variant wheels last, as they feature no variant properties. The group of non-variant wheels MUST be placed after all the other groups.
Within every group, the wheels MUST then be ordered according to their platform compatibility tags. After this process, the variant wheels are sorted from the most preferred to the least preferred.
The tools MAY provide options to override the default ordering, for example by specifying a preference for specific namespaces, features or properties. The tools MAY also provide options to exclude specific variants, or to select a particular variant.
Alternatively, the sort algorithm for variant wheels could be described using the following pseudocode. For simplicity, this code does not account for non-variant wheels or the subsequent ordering by platform compatibility tags.
Four new environment markers are introduced in dependency specifications:
The markers evaluating to sets of strings MUST be matched via the in or not in operator, e.g.:
The variant_label marker is a plain string:
Implementations MUST ignore differences in whitespace while matching the features and properties.
Variant marker expressions MUST be evaluated against the variant properties stored in the wheel being installed.
Variant wheels can be listed in pylock.toml file in the same manner as wheels with different Platform compatibility tags: either all variant (and non-variant) wheels can be listed, or a subset of them.
A new [packages.variants-json] subtable is added to the file. It MUST inline the contents of the index-level metadata file, converting the JSON structure into the respective TOML types. The $schema key MUST be preserved to facilitate versioning. The tools MAY remove keys that are not relevant to variant wheels present in pylock.toml.
If variant wheels are listed, the tool SHOULD resolve variants to select the best wheel file.
The proposed text for pylock.toml Specification follows:
[packages.variant_json]When asked to install a version of a package from an index, the proposed behavior would be to:
Note that steps 4. through 8. are introduced specifically for variant wheels. The remaining steps correspond to the current installer behavior.
When installing from a source that does not provide an index-level metadata, the same algorithm can be used, except that the variant metadata needs to be read directly from the wheels.
When asked to install a local wheel file, the proposed behavior would be to:
Variant wheels are uploaded to an index just like regular wheels. There are two possible approaches to publishing the index-level {name}-{version}-variants.json file for every package version: it can either be prepared and uploaded by the user, or it can be generated automatically by the index.
If the index is responsible for generating the file, it should use some mechanism to defer publishing it until the release is fully uploaded (for example, PEP 694).
To generate the {name}-{version}-variants.json file:
This PEP is part of a larger variant wheel design that was originally proposed as PEP 817. However, due to its complexity, we decided to split it into smaller parts that build one upon another. This PEP is the first in the series, providing foundations including the file format along with necessary metadata, index support and basic tool algorithms. Aspects such as providing actual variant properties or building wheels are deferred into subsequent PEPs.
Variant wheels use structured variant properties to express multidimensional wheel compatibility matrices. Properties are organized in namespaces that can be defined and governed independently. The key-value structure makes the properties more flexible: adding a new compatibility axis can be done by adding a new key. It can support both AND-style dependencies (for example, a CPU plugin could define multiple keys corresponding to different instructions sets, all of which are used in the package and therefore must be supported) and OR-style dependencies (for example, a GPU plugin can define a single key listing multiple GPU types, indicating that all of them are supported by the package, and therefore the users needs to own only one of them).
The specification does not impose any formal limits on the number of properties expressed, and specifically accounts for the possibility of property sets being very long (for example, a long list of GPUs or CPU extension sets). To avoid wheel filenames becoming hard to comprehend because of excess of information and potentially causing technical issues because of their length, the property lists are stored inside the wheel and mapped to a short label that is chosen by the package maintainer and intended to be human-readable.
Wheel filenames alone do not provide sufficient metadata to drive variant wheel selection. To avoid tools having to fetch the variant metadata straight from multiple wheel files, the metadata from wheels for every package version is combined and republished. This metadata is scoped to a single package version to permit variants changing in the future version.
The index support aims to account for three scenarios:
Since JSON format does not feature a set type, sets in the metadata are represented as sorted lists. Sorting ensures reproducibility and makes it possible to use equality comparison over whole dictionaries without having to convert specific fields back to sets after deserialization.
The variant ordering algorithm has been proposed with the assumption that variant properties take precedence over Platform compatibility tags, as they are primarily used to express user preferences. This accounts for possible divergence of platform tags, e.g. because a CUDA variant may require a different minimal libc version, in which case the selection should be driven by the desired CUDA preference rather than incidental platform tag difference.
While a future PEP will define how variant properties are provided, a baseline assumption is made that the compatible properties will be provided in specific order corresponding to their preference. This makes it possible to use a generic sorting algorithm, and later define properties as data without having to change the algorithm.
A future PEP will define how the ordering for features and values is provided. However, namespaces are governed independently and considered on equal footing, and therefore there will be no standard ordering for them. Instead, the ordering of namespaces will be explicitly stated in the variants metadata, which in turn will be provided by the package maintainer as part of the build process. For completeness, it will also be possible to provide overrides for the ordering of features and values via the same mechanism.
In the vast majority of real use cases, ordering based on properties will suffice. However, in a pathological case two different variant wheels may end up with equal sort keys. To provide reproducible results in this case, fallback sorting on variant label is performed.
A concept of null variant is introduced that is distinct from non-variant wheels to facilitate a transition period. This variant is always supported by tools implementing this PEP, and takes precedence over non-variant wheel. It can therefore be used to provide a distinct fallback for the cases of no other variant being supported and variant wheels being unsupported altogether. For example, PyTorch could provide a much smaller null variant that is used when no GPU is supported, and a fallback non-variant wheel built for the default CUDA version.
pylock.toml integration inlines the variant metadata to keep the file standalone. This can avoid the additional network call that would be required to fetch the file, and avoids having to pin to a specific hash that could cause problems if the file changed on the index, either due to the variant metadata being updated or being generated in a way that does not guarantee stable bytewise output.
Variant wheels add an additional variant label component to the wheel filename. A complete filename verification step should reject such wheels:
Currently, no Python tags start with a digit. To guarantee unambiguity, the specification enforces that going forward. Tools commonly used to install wheels at the time of writing implemented a verification algorithm of that kind, making it possible to publish variant wheels on an index alongside non-variant wheels without risk of them being installed accidentally.
Tools that do not perform full filename verification will consume some or all variant wheels as regular wheels. This may cause unexpected behavior or breakage if the tool in question needs to specially account for variant wheels.
The libraries for processing wheel files and their consumers will need to be updated to handle the new filename component and possibly the new metadata. For example, there is an open discussion in packaging project how to adapt the parse_wheel_filename() function.
The addition of the variant label increases the filename length. On platforms with a low total path length limit such as Windows, long filenames are a concern. However, given that the name and version components are already unrestricted, we do not set a specific limit in this PEP. Others, such as PyPI, may set a limit for total filename length.
Aside from this explicit incompatibility, the specification makes minimal and non-intrusive changes to the binary package format. The variant metadata is stored in a separate file in the .dist-info directory. Tools that are not directly concerned with variants need only to update their filename verification algorithm (if there is one) and preserve the contents of said directory.
If the new environment markers are used in wheel dependencies, these wheels will be incompatible with existing tools. For example, upon meeting these markers in a dependency from an index, pip will backtrack and use an older dependency version (if possible). This is a general problem with the design of environment markers, and not specific to wheel variants. It is possible to work around it by partially evaluating environment markers at build time, and removing the markers or dependencies specific to variant wheels from the non-variant wheel.
The presence of variant wheels may lead to some of the variants being subject to less scrutiny than others, and as such becoming easier attack targets. Particularly, once variant wheel support becomes commonplace, the non-variant wheels for some packages may be only consumed by users with outdated tools. However, such attacks assume that the package publishing workflow is already compromised, in which case more plausible attack vectors are available, for example via modifying compiled extensions.
This PEP is oriented at tool authors. Its changes will be integrated into Binary distribution format and other PyPA specifications. Teaching variants to end users will be covered in a subsequent PEP, as user experience details are addressed.
The variantlib project contains a reference implementation of a complete variant wheel solution. It is compliant with this PEP, but also goes beyond it, providing example solutions to some of the deferred items.
A client for installing variant wheels is implemented in a uv branch.
The specification proposes that variant labels are arbitrary, and variant properties are mapped to them via a variant metadata file rather than expressed directly in them. While it could be technically possible to create variant labels from variant properties, this would either require permitting very long filenames that will cause issues with some platforms, or imposing arbitrary limits on variant property counts, making the specification less suitable for addressing multidimensional compatibility matrices.
An alternative approach was to use a hash of variant properties. While such an approach is technically valid and can provide short unique labels for arbitrarily large variant property sets, it makes the labels opaque and therefore difficult to read or reason about.
The specification adds the variant label as a separate component, therefore breaking compatibility with existing tools. It could be technically possible to preserve partial compatibility by appending it to one of the Platform compatibility tags instead, in which case installers would reject the wheel based on platform (or Python interpreter) incompatibility, while other tools could still use it. However, the authors decided it safer to break the backwards compatibility. Additionally, reusing tags posed a potential risk of wheel labels being incorrectly combined with compressed tag sets. For example, a manylinux_2_27_x86_64.manylinux_2_28_x86_64+x86_64_v3 tag would be incorrectly deemed compatible because of the manylinux_2_27_x86_64 part.
Technically, it would be entirely possible to convey the information currently passed via the Platform compatibility tags via variant properties, and remove these explicit tags from the filename. However, we decided not to pursue this and instead preserve the existing filenames for wheels that do not need additional variants, as we do not believe that the effort required to update all the existing workflows justifies the benefit of more compact, and slightly more consistent naming.
The following problems are deferred to subsequent PEPs in the series:
In addition to that, the following matters are left implementation-defined:
This work would not have been possible without the contributions and feedback of many people in the Python packaging community. In particular, we would like to credit the following individuals for their help in shaping this PEP (in alphabetical order):
Alban Desmaison, Bradley Dice, Chris Gottbrath, Dmitry Rogozhkin, Emma Smith, Geoffrey Thomas, Henry Schreiner, Jeff Daily, Jeremy Tanner, Jithun Nair, Keith Kraus, Leo Fang, Mike McCarty, Nikita Shulga, Paul Ganssle, Philip Hyunsu Cho, Robert Maynard, Vyas Ramasubramani, and Zanie Blue.
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.
Source: https://github.com/python/peps/blob/main/peps/pep-0825.rst
Last modified: 2026-07-02 16:07:20 UTC