From 499e0f7652167536a26c0bfbb31d6e570649745d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Thu, 19 Feb 2026 20:06:34 +0100 Subject: [PATCH 01/28] PEP xxxx: Wheel Variants: Providers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the second PEP split off PEP 817. Its focus is on how variant properties are governed and how their compatibility is determined. This is primarily done via opt-in plugins that are Python packages specified in variant metadata, but can also be vendored or reimplemented by the tools. Package maintainers and users can only supply static compatibility data to avoid the need for plugins. Compared to PEP 817, the provider metadata has been largely simplified by removing all the bits deemed not strictly necessary, and provider plugins have been made opt-in (with provisions for tools to make some of them opt-out, at their leisure). The recommendations for governance of opt-out providers and building variant wheels will follow in subsequent PEPs. Signed-off-by: Michał Górny --- peps/pep-9999.rst | 743 ++++++++++++++++++ .../pep-9999/appendix-variant-json-schema.rst | 11 + peps/pep-9999/variant-schema-0.2.0.json | 176 +++++ 3 files changed, 930 insertions(+) create mode 100644 peps/pep-9999.rst create mode 100644 peps/pep-9999/appendix-variant-json-schema.rst create mode 100644 peps/pep-9999/variant-schema-0.2.0.json diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst new file mode 100644 index 00000000000..af242cf2b1a --- /dev/null +++ b/peps/pep-9999.rst @@ -0,0 +1,743 @@ +PEP: 9999 +Title: Wheel Variants: Providers +Author: Jonathan Dekhtiar , + Michał Górny , + Konstantin Schütze , + Ralf Gommers , + Andrey Talman , + Charlie Marsh , + Michael Sarahan , + Eli Uriegas , + Barry Warsaw , + Donald Stufft , + Andy R. Terrel +Discussions-To: https://discuss.python.org/t/pep-817-split-wheel-variants-package-format/106196 +Status: Draft +Type: Standards Track +Topic: Packaging +Created: 17-Feb-2026 +Post-History: `17-Feb-2026 `__ + +Abstract +======== + +This PEP is a followup to :pep:`825` that defines how variant properties +are governed and how their compatibility is determined. This is +primarily done via opt-in plugins that are Python packages specified in +variant metadata, but can also be vendored or reimplemented by the +tools. Package maintainers and users can only supply static +compatibility data to avoid the need for plugins. + + +Motivation +========== + +:pep:`825` introduced a protocol for recording additional compatibility +data in binary packages, in the form of variant properties. It provided +the foundations by organizing the variant properties into namespaces. +However, it did not specify how variant namespaces are governed, nor how +to determine which variant properties are compatible with a particular +use system. This PEP aims to fill this gap. + +The PEP is specifically aiming to make variant wheels suitable for +satisfying three use cases: + +1. Variants that express platform compatibility, for example GPU or CPU + capabilities. In this case, the goal is to select the best wheel that + is compatible with the particular system. + +2. Variants that express non-platform properties, such as different + BLAS/LAPACK or OpenMP implementations, or debug builds. Here all + variants that were built are compatible, and the goal is to provide + user with the ability to explicitly select a non-default variant. + +3. Variants that express compatibility with different dependency + versions, particularly aiming to express Application Binary Interface + (ABI) compatibility. The goal is to enable matching variants against + other packages, especially if they adapt new versions of common + dependencies at different rates. + + +Specification +============= + +Definitions +----------- + +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`. + + +Providers +--------- + +Variant properties, as defined in :pep:`825`, are organized into variant +namespaces. Every variant namespace used in a variant wheel MUST be +governed by a *variant provider*. Variant providers supply ordered lists +of compatible features and feature values corresponding to their +namespaces, as required by :pep:`825`. + +The namespace ``abi_dependency`` is reserved for the `ABI Dependency +Variant Provider <#abi-dependency-variant-provider-optional>`_. All +other namespaces used MUST be defined in the `provider information`_ +dictionary in `variant metadata`_. For every namespace, the following +rules apply, in order: + +1. A variant provider MAY be enabled or disabled by default. The tools + MUST provide a way to explicitly enable a provider, and MAY provide a + way to disable one. If a provider is disabled, the tools MUST assume + that the list of compatible features is empty and they MUST NOT use + the provider in any way. Otherwise, proceed to step 2. + +2. A variant provider metadata MAY include a static list of compatible + features and their values. If that is the case, the tools MUST use + the static lists. Otherwise, proceed to step 3. + +3. The tools SHOULD implement a way for the user to provide a static + list of compatible features and their values. If the user provides + said list, the tools MUST use it. Otherwise, proceed to step 4. + +4. If no static list of compatible features and their values is + provided, the variant provider metadata MUST specify a list of + required Python packages that provide a variant provider plugin. The + tools MAY choose to vendor or reimplement plugin packages at their + leisure. If that is the case, they MUST obtain the list from their + implementation. Otherwise, proceed to step 5. + +5. The tools MAY provide list of trusted packages that are permitted to + be used by default. They SHOULD provide a way for the user to trust + additional packages. If the list of required provider packages + contains any untrusted package, the tools MUST assume that the list + of compatible features is empty and they MUST NOT install or use the + provider packages. Otherwise, proceed to step 6. + +6. The tools MUST install the specified provider packages and query them + via the `provider plugin API`_. The tools SHOULD use an isolated + virtual environment for that purpose. + + +Variant metadata +---------------- + +This PEP extends the metadata introduced in :pep:`825` with an +additional ``providers`` key. Therefore, the metadata has the following +structure: + +.. code:: text + + (root) + | + +- $schema + +- default-priorities + +- variants + +- providers + +- {namespace} + +- optional : bool = False + +- plugin-api : str | None = None + +- requires : list[str] = [] + +- static-properties + +- {feature} : list[str] = [] + +This structure corresponds to the version ``0.2.0`` of the format. An +update of the proposed JSON schema for the current format version is +included in the Appendix of this PEP. The schema is available in +:ref:`pep9999-variant-json-schema`. + + +Provider information +'''''''''''''''''''' + +``providers`` is a dictionary, the keys are namespaces, the values are +dictionaries with provider information. It specifies how to install and +use variant providers. Its set of keys MUST match the set of values in +the ``default-priorities.namespace`` list of variant metadata. + +The use of provider information is described in the `Providers`_ and +`Provider plugin API`_ sections. + +One of the following keys MUST be present in the provider information +dictionary: + +- ``static-properties: dict[str, list[str]]``: The dictionary of static + variant compatibility data. The keys correspond to feature names, + while the values are the ordered list of compatible feature values. + Since the dictionaries in JSON are unsorted, if more than one key is + specified, then the order for all features MUST be specified in + ``default-priorities.feature.{namespace}``. + +- ``requires: list[str]``: A list of zero or more package + :ref:`dependency specifiers `, that are used to + install the provider plugin. If the dependency specifiers include + environment markers, these are evaluated against the environment where + the plugin is being installed, and the requirements for which the + markers evaluate to false are filtered out. At least one dependency + MUST remain present in every possible environment after filtering. + Additionally, if ``plugin-api`` is not specified, the first dependency + present after filtering MUST always evaluate to the same API endpoint. + +If both keys are provided, the ``requires`` key MUST be ignored. + +A provider information dictionary MAY additionally contain the following +keys: + +- ``optional: bool``: Whether the provider is optional. Defaults + to ``false``. If it is ``true``, the provider is disabled by default + and needs to be enabled explicitly. + +- ``plugin-api: str``: The API endpoint for the plugin. If it is + specified, it MUST be an object reference as explained in the `API + endpoint`_ section. If it is missing, the package name from the first + dependency specifier in ``requires`` is used, after replacing all + ``-`` characters with ``_`` in the normalized package name. + + +Example +''''''' + +.. code:: json5 + + { + // The schema URL will be replaced with the final URL on packaging.python.org + "$schema": "https://variants-schema.wheelnext.dev/v0.0.3.json", + + "default-priorities": { + // MUST list all namespaces used. + "namespace": ["x86_64", "aarch64", "blas_lapack"], + + // other fields same as in PEP 825 + "feature": {}, + "property": {} + }, + + "providers": { + // MUST list all namespaces used. + "aarch64": { + // Specifies provider plugin package. REQUIRED since there is no + // "static-properties" -- plugin will be queried at install time. + "requires": ["provider-variant-aarch64 >=0.0.1"], + // Overrides plugin API endpoint. OPTIONAL; without it, the API + // endpoint "provider_variant_aarch64" would be used. + "plugin-api": "provider_variant_aarch64.plugin:AArch64Plugin" + }, + "blas_lapack": { + // Specifies compatible properties. No install-time querying is + // necessary. + "static-properties": { + // Values are ordered -- by default, openblas variant is preferred + // over mkl, and accelerate over the other two. + "library": ["accelerate", "openblas", "mkl"] + }, + // OPTIONAL: unused since "static-properties" are available. + "requires": ["blas-lapack-variant-provider"] + }, + "x86_64": { + // Specifies provider plugin package. REQUIRED since there is no + // "static-properties" -- plugin will be queried at install time. + "requires": [ + "provider-variant-x86-64 >=0.0.1; python_version >= '3.12'", + "legacy-provider-variant-x86-64 >=0.0.1; python_version < '3.12'" + ], + // Overrides plugin API endpoint. REQUIRED since "requires" may + // evaluate to two different packages depending on Python version. + "plugin-api": "provider_variant_x86_64.plugin:X8664Plugin" + } + }, + + "variants": { + // same as in PEP 825 + } + } + + +Provider plugin API +------------------- + +High level design +''''''''''''''''' + +All variants published on a single index for a specific package version +MUST use the same provider for a given namespace. Attempting to load +more than one plugin for the same namespace in the same release version +MUST result in a fatal error. While multiple plugins for the same +namespace MAY exist across different packages, release versions or +indexes (such as when a plugin is forked due to being unmaintained), +they are mutually exclusive within any single release version on an +index. + +To make it easier to discover and install plugins, they SHOULD be +published in the same indexes that the packages using them. In +particular, packages published to PyPI MUST NOT rely on plugins that +need to be installed from other indexes. + +Except for namespaces reserved as part of this PEP, installable Python +packages MUST be provided for plugins. The entire dependency tree of +such a plugin MUST be installable using non-variant wheels, and variant +wheels MUST NOT be used. + +As noted in the `Providers`_ section, these plugins can also be +reimplemented by tools needing them. In the latter case, the resulting +reimplementation does not need to follow the API defined in this +section. + +A plugin implemented as Python package exposes callables that are called +via: + +.. code:: text + + {API endpoint}.{callable name}({arguments}...) + +These can be implemented either as module-level functions, class methods +or static methods. The specifics are provided in the subsequent +sections. + + +API endpoint +'''''''''''' + +The location of the plugin code is called an "API endpoint", and it is +expressed using the object reference notation following the +:doc:`packaging:specifications/entry-points`: + +.. code:: text + + {import_path}(:{object_path})? + +An API endpoint specification is equivalent to the following Python +pseudocode: + +.. code:: python + + import {import_path} + + if "{object_path}": + plugin = {import_path}.{object_path} + else: + plugin = {import_path} + +API endpoints are used in two contexts: + +a. in the ``plugin-api`` key of variant metadata, either explicitly or + inferred from the package name in the ``requires`` key. This is the + primary method of using the plugin when building and installing + wheels. + +b. as the value of an installed entry point in the ``variant_plugins`` + group. The name of said entry point is insignificant. This is + OPTIONAL but RECOMMENDED, as it permits variant-related utilities to + discover variant plugins installed to the user's environment. + + +Variant feature config class +'''''''''''''''''''''''''''' + +The variant feature config class is used as a return value in plugin API +functions. It defines a single variant feature, along with a list of +possible values. Depending on the context, the order of values MAY be +significant. It is defined using the following protocol: + +.. code:: python + + from abc import abstractmethod + from typing import Protocol + + + class VariantFeatureConfigType(Protocol): + @property + @abstractmethod + def name(self) -> str: + """Feature name""" + raise NotImplementedError + + @property + @abstractmethod + def multi_value(self) -> bool: + """Does this property allow multiple values per variant?""" + raise NotImplementedError + + @property + @abstractmethod + def values(self) -> list[str]: + """List of values, possibly ordered from most preferred to least""" + raise NotImplementedError + +The instance MUST provide the following properties or attributes: + +- ``name: str`` specifying the feature name. + +- ``multi_value: bool`` specifying whether the feature is allowed to + have multiple corresponding values within a single variant wheel. If + it is ``False``, then it is an error to specify multiple values for + the feature. + +- ``values: list[str]`` specifying feature values. In contexts where the + order is significant, the values MUST be ordered from the most + preferred to the least preferred. + + +Plugin interface +'''''''''''''''' + +The plugin interface MUST follow the following protocol: + +.. code:: python + + from abc import abstractmethod + from typing import Protocol + + + class PluginType(Protocol): + @classmethod + @abstractmethod + def get_supported_configs(cls) -> list[VariantFeatureConfigType]: + """Get ordered lists of compatible features and their values""" + raise NotImplementedError + +The plugin interface MUST provide the following function: + +- ``get_supported_configs() -> list[VariantFeatureConfigType]`` that + returns a list of feature names and their values that are compatible + with the system the plugin is running on. The variant feature and + value lists MUST be ordered from the most preferred to the least + preferred. + + +Example implementation +'''''''''''''''''''''' + +.. code:: python + + from dataclasses import dataclass + + + @dataclass + class VariantFeatureConfig: + name: str + values: list[str] + multi_value: bool + + + # internal -- provided for illustrative purpose + _ALL_GPUS = ["narf", "poit", "zort"] + + + def _get_current_version() -> int: + """Returns currently installed runtime version""" + ... # implementation not provided + + + def _is_gpu_available(codename: str) -> bool: + """Is specified GPU installed?""" + ... # implementation not provided + + + class MyPlugin: + @staticmethod + def get_supported_configs() -> list[VariantFeatureConfig]: + current_version = _get_current_version() + if current_version is None: + # no runtime found, system not supported at all + return [] + + return [ + VariantFeatureConfig( + name="min_version", + # [current, current - 1, ..., 1] + values=[str(x) for x in range(current_version, 0, -1)], + multi_value=False, + ), + VariantFeatureConfig( + name="gpu", + # this may be empty if no GPUs are supported -- + # 'example :: gpu feature' is not supported then; + # but wheels with no GPU-specific code and only + # 'example :: min_version' could still be installed + values=[x for x in _ALL_GPUS if _is_gpu_available(x)], + multi_value=True, + ), + ] + + +Future extensions +''''''''''''''''' + +The future versions of this specification, as well as third-party +extensions MAY introduce additional attributes on the plugin instances. +The implementations SHOULD ignore them. + +For best compatibility, all private attributes SHOULD be prefixed with +an underscore (``_``) character to avoid incidental conflicts with +future extensions. + + +ABI Dependency Variant Provider (Optional) +------------------------------------------- + +This section describes an OPTIONAL extension to the wheel variant +specification. Tools that choose to implement this feature MUST follow +this specification. Tools that do not implement this feature MUST treat +the variants using it as incompatible, and SHOULD inform users when such +wheels are skipped. + +The variant namespace ``abi_dependency`` is reserved for expressing that +different builds of the same version of a package are compatible with +different versions or version ranges of a dependency. This namespace +MUST NOT be listed in the `provider information`_ dictionary, and can +only appear in a built wheel variant property. + +Within this namespace, zero or more properties can be used to express +compatible dependency versions. For each property, the feature name MUST +be the :ref:`normalized name ` of the +dependency, whereas the value MUST be a valid release segment of +a public version identifier, as defined by the +:doc:`packaging:specifications/version-specifiers` specification. +It MUST contain up to three version components, that are matched against +the installed version same as the ``=={value}.*`` specifier. Notably, +trailing zeroes match versions with fewer components (e.g. ``2.0`` +matches release ``2`` but not ``2.1``). This also implies that the +property values have different semantics than PEP 440 versions, in +particular ``2``, ``2.0`` and ``2.0.0`` represent different ranges. + +Versions with nonzero epoch are not supported. + +==================================== ================== +Variant Property Matching Rule +==================================== ================== +``abi_dependency :: torch :: 2`` ``torch==2.*`` +``abi_dependency :: torch :: 2.9`` ``torch==2.9.*`` +``abi_dependency :: torch :: 2.8.0`` ``torch==2.8.0.*`` +==================================== ================== + +Multiple variant properties with the same feature name can be used to +indicate wheels compatible with multiple providing package versions, +e.g.: + +.. code:: text + + abi_dependency :: torch :: 2.8.0 + abi_dependency :: torch :: 2.9.0 + +This means the wheel is compatible with both PyTorch 2.8.0 and 2.9.0. + + +Rationale +========= + +The primary use case for providers is determining platform +compatibility, which implies that they need to be used at install time. +The specification proposes a plugin mechanism using Python packages, +with the interface inspired by :pep:`517`. Such a mechanism has a few +advantages: + +- The individual plugins can be governed independently, by the + stakeholders having necessary knowledge and hardware. Additional + compatibility axes (new CPUs, GPUs) do not impose direct maintenance + costs on tools interacting with variant wheels, nor on + centrally-maintained libraries such as ``packaging``. + +- The plugins can be updated as frequently as necessary, without being + tied to tool release schedules. + +- The plugins provide a unified interface for testing new providers. New + plugins can be developed and tested locally without having to patch + multiple tools, and released to the public after proving the concept. + +At the same time, it is understood that installing additional Python +packages and running the code from them at install time introduces +additional attack vector (as discussed in `security implications`_). +For this reason, plugin packages are entirely opt-in, and a few +mechanisms are provided to improve the user experience without +compromising security: + +- Users can provide static compatibility lists to avoid querying the + providers. This also permits deploying packages for different systems + than the one running the installer. + +- Tools can maintain their own lists of trusted provider plugins that + are enabled by default, or they can vendor or reimplement some + providers. This is entirely voluntary, as not to impose maintenance + effort on tool maintainers. At the same time, the ability to + reimplement providers avoids introducing a performance bottleneck on + tools that aren't written in Python. + +- Variant wheels can include static lists of compatible properties, to + facilitate variants that do not need querying platform capabilities, + such as builds done against different BLAS/LAPACK libraries. + +Furthermore, individual providers can be disabled by default (made +optional), to introduce variants that can only be selected explicitly, +for example debug or experimental builds of packages. + +Installing provider plugins in isolated environments is recommended, as +that permits tools to automatically deploy them without affecting the +system packages. However, this is not a requirement to permit other +options. For example, build backends may prefer reusing the isolated +build environment for this. + +The ``requires`` and ``plugin-api`` keys follow the precedent of +``build-system.requires`` and ``build-system.build-backend`` keys of +:pep:`517`. However, following the criticism of that design, +``plugin-api`` has been made optional and defaults to being inferred +from the package name. + +The `ABI Dependency Variant Provider +<#abi-dependency-variant-provider-optional>`_ is defined separately, as +it needs to interact with the dependency resolver. To avoid adding +significant complexity to the plugin API and at the same time +restricting the actual implementation, it has been made a special case. +It is entirely optional to avoid adding maintenance burden to tool +maintainers. + + +Backwards Compatibility +======================= + +This PEP does not introduce any new backwards compatibility +considerations, compared to :pep:`825`. + + +Security Implications +===================== + +This PEP introduces a plugin system for querying the platform +capabilities. Tools may install these packages and execute the code +within them during dependency resolution or wheel processing. This +elevates the supply-chain attack potential by introducing two new points +for malicious actors to inject arbitrary code payload: + +1. Publishing a version of a variant provider plugin or one of its + dependencies with malicious code. +2. Introducing a malicious variant provider plugin in an existing + package metadata. + +Admittedly, such attacks can already be done to the package's +dependencies. However, in some cases the affected tools are executed +with elevated privileges (such as when installing packages for +multi-user systems), while the package itself will only be run with +regular user privileges. Therefore, variant provider plugins could +introduce a Remote Code Execution vulnerability with elevated +privileges. + +A similar issue already exists in the packaging ecosystem when packages +are installed from source distributions, with build backends and other +build dependencies are being installed and executed. However, various +tools operating purely on wheels, as well as users using tool-specific +options to disable the use of source distributions, have been relying on +the assumption that no such code execution will happen. To uphold this +assumption, the proposal makes plugin packages opt-in. + +Unfortunately, an opt-in system creates a risk of security fatigue. +Users wishing to use variant wheels may start blanket-enabling all use +of provider plugins, reintroducing the RCE danger. To avoid this and +improve user experience, the PEP permits tool maintainers to provide +opt-out experience for selected plugins. A subsequent PEP will give +further recommendations for improved user experience. + + +How to Teach This +================= + +This PEP is focused on installing variant wheels. The primary source of +information for the users will be the user interface of installers, +supplemented by their documentation and installation instructions of +specific packages publishing variant wheels. The documentation present +on ``packaging.python.org`` will need to be updated as well. + +Ideally, in the most common use cases variants will work out of the box +and non-expert users will not need to be aware of them, much like they +do not need to be aware of Platform compatibility tags. + +Expert users will need guidance that will largely be specific to +particular installer implementation, as it involves user interface +decisions. The following topics may need to be covered, depending on the +features implemented by the installer: + +- how to enable installing untrusted variant provider packages, and what + are the security implications of that +- how to enable optional providers +- how to generate and provide static compatibility data, enabling + deployment for remote targets +- how to explicitly select a specific variant +- how to alter variant selection, for example by specifying preferred + properties or filtering out undesirable properties + +The topic of teaching package maintainers will be addressed in a +subsequent PEP, along with building variant wheels. + + +Reference Implementation +======================== + +The `variantlib `__ project +contains a reference implementation of this PEP. + +A client for installing variant wheels is implemented in a +`uv branch `__. + + +Rejected Ideas +============== + +An approach without provider plugins +------------------------------------ + +Rather than introducing provider plugins, the rules governing every +variant namespace could be defined via PEPs. However, such an approach +would be less scalable and impose additional effort on stakeholders, PEP +editors and tool maintainers. + +Every new namespace would have to go through standardization process, +followed by explicit implementation process. Deployment of new variant +properties would be entirely dependent on tool updates. The added +maintenance cost could lead to support for less popular variant axes not +being accepted, or lack of feature parity between different tools. + + +Open Issues +=========== + +This PEP is concerned with installing wheels only. A subsequent PEP will +address building variant wheels. + + +Acknowledgements +================ + +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. + + +Change History +============== + +- 17-Feb-2026 + + - Initial version, split from :pep:`817` draft. + - Namespaces have been removed from the `provider plugin API`_. + Instead, the namespace is named by the package in `variant + metadata`_. + - The ``enable-if`` key has been removed from `provider information`_, + as it was deemed redundant. + - The ``static-properties`` table has been moved into `provider + information`_. + + +Appendices +========== + +- :ref:`pep9999-variant-json-schema` + + +Copyright +========= + +This document is placed in the public domain or under the +CC0-1.0-Universal license, whichever is more permissive. diff --git a/peps/pep-9999/appendix-variant-json-schema.rst b/peps/pep-9999/appendix-variant-json-schema.rst new file mode 100644 index 00000000000..b5e05c1f4b2 --- /dev/null +++ b/peps/pep-9999/appendix-variant-json-schema.rst @@ -0,0 +1,11 @@ +:orphan: + +.. _pep9999-variant-json-schema: + +Appendix: JSON Schema for Variant Metadata +========================================== + +.. literalinclude:: variant-schema-0.2.0.json + :language: json + :linenos: + :name: variant-json-schema diff --git a/peps/pep-9999/variant-schema-0.2.0.json b/peps/pep-9999/variant-schema-0.2.0.json new file mode 100644 index 00000000000..a1b1eec162e --- /dev/null +++ b/peps/pep-9999/variant-schema-0.2.0.json @@ -0,0 +1,176 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://variants-schema.wheelnext.dev/peps/826/v0.2.0.json", + "title": "Variant metadata, v0.2.0", + "description": "The format for variant metadata (variant.json) and index-level metadata ({name}-{version}-variants.json)", + "type": "object", + "properties": { + "$schema": { + "description": "JSON schema URL", + "type": "string" + }, + "default-priorities": { + "description": "Default priorities for ordering variants", + "type": "object", + "properties": { + "namespace": { + "description": "Namespaces (in order of preference)", + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z0-9_]+$" + }, + "minItems": 1, + "uniqueItems": true + }, + "feature": { + "description": "Default feature priorities (by namespace)", + "type": "object", + "patternProperties": { + "^[a-z0-9_]+$": { + "description": "The most preferred features", + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z0-9_]+$" + }, + "minItems": 0, + "uniqueItems": true + } + }, + "additionalProperties": false, + "uniqueItems": true + }, + "property": { + "description": "Default property priorities (by namespace)", + "type": "object", + "patternProperties": { + "^[a-z0-9_]+$": { + "description": "Default property priorities (by feature name)", + "type": "object", + "patternProperties": { + "^[a-z0-9_]+$": { + "description": "The most preferred feature values", + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z0-9_.]+$" + }, + "minItems": 0, + "uniqueItems": true + } + }, + "additionalProperties": false, + "uniqueItems": true + } + }, + "additionalProperties": false, + "uniqueItems": true + } + }, + "additionalProperties": false, + "uniqueItems": true, + "required": [ + "namespace" + ] + }, + "providers": { + "description": "Mapping of namespaces to provider information", + "type": "object", + "patternProperties": { + "^[A-Za-z0-9_]+$": { + "type": "object", + "description": "Provider information", + "properties": { + "plugin-api": { + "description": "Object reference to plugin class", + "type": "string", + "pattern": "^([a-zA-Z0-9._]+ *: *[a-zA-Z0-9._]+)|([a-zA-Z0-9._]+)$" + }, + "optional": { + "description": "Whether the provider is optional (disabled by default)", + "type": "boolean" + }, + "requires": { + "description": "Dependency specifiers for how to install the plugin", + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 0, + "uniqueItems": true + }, + "static-properties": { + "description": "Static properties (by feature name)", + "type": "object", + "patternProperties": { + "^[a-z0-9_]+$": { + "description": "Ordered value list", + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z0-9_.]+$" + }, + "minItems": 0, + "uniqueItems": true + }, + "additionalProperties": false, + "uniqueItems": true + } + } + }, + "additionalProperties": false, + "uniqueItems": true, + "anyOf": [ + {"required": ["requires"]}, + {"required": ["static-properties"]} + ] + } + }, + "additionalProperties": false, + "uniqueItems": true + }, + "variants": { + "description": "Mapping of variant labels to properties", + "type": "object", + "patternProperties": { + "^[a-z0-9_.]{1,16}$": { + "type": "object", + "description": "Mapping of namespaces in a variant", + "patternProperties": { + "^[a-z0-9_.]+$": { + "description": "Mapping of feature names in a namespace", + "patternProperties": { + "^[a-z0-9_.]+$": { + "description": "List of values for this variant feature", + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z0-9_.]+$" + }, + "minItems": 1, + "uniqueItems": true + } + }, + "uniqueItems": true, + "additionalProperties": false + } + }, + "uniqueItems": true, + "additionalProperties": false + } + }, + "additionalProperties": false, + "uniqueItems": true + } + }, + "required": [ + "$schema", + "default-priorities", + "providers", + "variants" + ], + "additionalProperties": false, + "uniqueItems": true +} From 698f8325b766d028b3369efc1214adc6e89d0a2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Mon, 2 Mar 2026 14:48:59 +0100 Subject: [PATCH 02/28] Update headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index af242cf2b1a..47fda1afe55 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -11,12 +11,13 @@ Author: Jonathan Dekhtiar , Barry Warsaw , Donald Stufft , Andy R. Terrel -Discussions-To: https://discuss.python.org/t/pep-817-split-wheel-variants-package-format/106196 +Discussions-To: Pending Status: Draft Type: Standards Track Topic: Packaging -Created: 17-Feb-2026 -Post-History: `17-Feb-2026 `__ +Requires: 825 +Created: 02-Mar-2026 +Post-History: Pending Abstract ======== From 0fcae4db9de6a815d71987545eb635f25faf49e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Mon, 2 Mar 2026 17:06:46 +0100 Subject: [PATCH 03/28] Update schema URL in example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index 47fda1afe55..b4edf8ee8f1 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -200,7 +200,7 @@ Example { // The schema URL will be replaced with the final URL on packaging.python.org - "$schema": "https://variants-schema.wheelnext.dev/v0.0.3.json", + "$schema": "https://variants-schema.wheelnext.dev/peps/9999/v0.2.0.json", "default-priorities": { // MUST list all namespaces used. From e0fc5009b6b4f9f18c9807e1ded199e76544f492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Mon, 2 Mar 2026 18:14:18 +0100 Subject: [PATCH 04/28] Add a preface to the variant metadata example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index b4edf8ee8f1..7d29e49286b 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -196,6 +196,9 @@ keys: Example ''''''' +The ``foo-1.2.3-variants.json`` example from :pep:`825` can be extended +in the following way: + .. code:: json5 { From 85ddf618cdab6aa601bc6928151098a5664d636a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Mon, 2 Mar 2026 20:18:53 +0100 Subject: [PATCH 05/28] Make `requires` and `static-properties` mutually exclusive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index 7d29e49286b..e7b6d75422a 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -157,8 +157,8 @@ the ``default-priorities.namespace`` list of variant metadata. The use of provider information is described in the `Providers`_ and `Provider plugin API`_ sections. -One of the following keys MUST be present in the provider information -dictionary: +Exactly one of the following keys MUST be present in the provider +information dictionary: - ``static-properties: dict[str, list[str]]``: The dictionary of static variant compatibility data. The keys correspond to feature names, @@ -177,21 +177,25 @@ dictionary: Additionally, if ``plugin-api`` is not specified, the first dependency present after filtering MUST always evaluate to the same API endpoint. -If both keys are provided, the ``requires`` key MUST be ignored. - A provider information dictionary MAY additionally contain the following -keys: +key: - ``optional: bool``: Whether the provider is optional. Defaults to ``false``. If it is ``true``, the provider is disabled by default and needs to be enabled explicitly. +If ``requires`` key is present, it MAY additionally contain the +following key: + - ``plugin-api: str``: The API endpoint for the plugin. If it is specified, it MUST be an object reference as explained in the `API endpoint`_ section. If it is missing, the package name from the first dependency specifier in ``requires`` is used, after replacing all ``-`` characters with ``_`` in the normalized package name. +It is invalid to specify ``requires`` or ``plugin-api`` if +``static-properties`` are present. + Example ''''''' @@ -232,8 +236,6 @@ in the following way: // over mkl, and accelerate over the other two. "library": ["accelerate", "openblas", "mkl"] }, - // OPTIONAL: unused since "static-properties" are available. - "requires": ["blas-lapack-variant-provider"] }, "x86_64": { // Specifies provider plugin package. REQUIRED since there is no From 36a2fd6c0587b33dfc72b0bb554d53888883d0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Mon, 2 Mar 2026 20:23:54 +0100 Subject: [PATCH 06/28] Update the example to demonstrate implicit `plugin-api` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index e7b6d75422a..e941fa1d090 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -184,8 +184,8 @@ key: to ``false``. If it is ``true``, the provider is disabled by default and needs to be enabled explicitly. -If ``requires`` key is present, it MAY additionally contain the -following key: +If the ``requires`` key is present, the dictionary MAY additionally +contain the following key: - ``plugin-api: str``: The API endpoint for the plugin. If it is specified, it MUST be an object reference as explained in the `API @@ -223,10 +223,9 @@ in the following way: "aarch64": { // Specifies provider plugin package. REQUIRED since there is no // "static-properties" -- plugin will be queried at install time. - "requires": ["provider-variant-aarch64 >=0.0.1"], - // Overrides plugin API endpoint. OPTIONAL; without it, the API - // endpoint "provider_variant_aarch64" would be used. - "plugin-api": "provider_variant_aarch64.plugin:AArch64Plugin" + "requires": ["provider-variant-aarch64 >=0.0.1"] + // "plugin-api" is OPTIONAL here. It is inferred from "requires": + // "plugin-api": "provider_variant_aarch64" }, "blas_lapack": { // Specifies compatible properties. No install-time querying is From 6c5f548064e71d6efb015944f94d9f4486bdd78f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Mon, 2 Mar 2026 20:32:38 +0100 Subject: [PATCH 07/28] Require at least one item in `requires` for simplicity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index e941fa1d090..9a1c59782bd 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -167,7 +167,7 @@ information dictionary: specified, then the order for all features MUST be specified in ``default-priorities.feature.{namespace}``. -- ``requires: list[str]``: A list of zero or more package +- ``requires: list[str]``: A list of one or more package :ref:`dependency specifiers `, that are used to install the provider plugin. If the dependency specifiers include environment markers, these are evaluated against the environment where From 29addd194f9efa6dae5e35b61c8eb8ca2fc38206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Tue, 3 Mar 2026 11:06:38 +0100 Subject: [PATCH 08/28] Add `install-time` removal to changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index 9a1c59782bd..8592d1d4c31 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -729,8 +729,8 @@ Change History - Namespaces have been removed from the `provider plugin API`_. Instead, the namespace is named by the package in `variant metadata`_. - - The ``enable-if`` key has been removed from `provider information`_, - as it was deemed redundant. + - The ``enable-if`` and ``install-time`` keys have been removed from + `provider information`_, as they were deemed redundant. - The ``static-properties`` table has been moved into `provider information`_. From 7687014a86db8ec6809df8dc09a262915b47d42e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Mon, 9 Mar 2026 14:11:53 +0100 Subject: [PATCH 09/28] Remove class from example for simplicity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 48 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index 8592d1d4c31..096347dfc9d 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -436,31 +436,29 @@ Example implementation ... # implementation not provided - class MyPlugin: - @staticmethod - def get_supported_configs() -> list[VariantFeatureConfig]: - current_version = _get_current_version() - if current_version is None: - # no runtime found, system not supported at all - return [] - - return [ - VariantFeatureConfig( - name="min_version", - # [current, current - 1, ..., 1] - values=[str(x) for x in range(current_version, 0, -1)], - multi_value=False, - ), - VariantFeatureConfig( - name="gpu", - # this may be empty if no GPUs are supported -- - # 'example :: gpu feature' is not supported then; - # but wheels with no GPU-specific code and only - # 'example :: min_version' could still be installed - values=[x for x in _ALL_GPUS if _is_gpu_available(x)], - multi_value=True, - ), - ] + def get_supported_configs() -> list[VariantFeatureConfig]: + current_version = _get_current_version() + if current_version is None: + # no runtime found, system not supported at all + return [] + + return [ + VariantFeatureConfig( + name="min_version", + # [current, current - 1, ..., 1] + values=[str(x) for x in range(current_version, 0, -1)], + multi_value=False, + ), + VariantFeatureConfig( + name="gpu", + # this may be empty if no GPUs are supported -- + # 'example :: gpu feature' is not supported then; + # but wheels with no GPU-specific code and only + # 'example :: min_version' could still be installed + values=[x for x in _ALL_GPUS if _is_gpu_available(x)], + multi_value=True, + ), + ] Future extensions From 3fdd68c97696ff1f7b3d35ba0bb6a986a8c6c8e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Mon, 9 Mar 2026 19:59:00 +0100 Subject: [PATCH 10/28] Explicitly forbid `abi_dependency` in priorities too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index 096347dfc9d..18e2c4b8827 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -485,8 +485,9 @@ wheels are skipped. The variant namespace ``abi_dependency`` is reserved for expressing that different builds of the same version of a package are compatible with different versions or version ranges of a dependency. This namespace -MUST NOT be listed in the `provider information`_ dictionary, and can -only appear in a built wheel variant property. +MUST NOT be listed in the `provider information`_ dictionary nor in the +``default-priorities.namespace`` dictionary, and can only appear in a +built wheel variant property. Within this namespace, zero or more properties can be used to express compatible dependency versions. For each property, the feature name MUST From ff31b5f65f0a53d1b7a589a38c3fa1264d8daead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Wed, 11 Mar 2026 17:13:25 +0100 Subject: [PATCH 11/28] Apply suggestions from code review Co-authored-by: Ralf Gommers --- peps/pep-9999.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index 18e2c4b8827..afce45d6ee6 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -50,7 +50,7 @@ satisfying three use cases: 2. Variants that express non-platform properties, such as different BLAS/LAPACK or OpenMP implementations, or debug builds. Here all variants that were built are compatible, and the goal is to provide - user with the ability to explicitly select a non-default variant. + users with the ability to explicitly select a non-default variant. 3. Variants that express compatibility with different dependency versions, particularly aiming to express Application Binary Interface @@ -91,7 +91,7 @@ rules apply, in order: that the list of compatible features is empty and they MUST NOT use the provider in any way. Otherwise, proceed to step 2. -2. A variant provider metadata MAY include a static list of compatible +2. A variant provider's metadata MAY include a static list of compatible features and their values. If that is the case, the tools MUST use the static lists. Otherwise, proceed to step 3. @@ -271,7 +271,7 @@ they are mutually exclusive within any single release version on an index. To make it easier to discover and install plugins, they SHOULD be -published in the same indexes that the packages using them. In +published in the same indexes as the packages using them. In particular, packages published to PyPI MUST NOT rely on plugins that need to be installed from other indexes. From bde46c9af6411ee4521cad31cc1ea8c6d28163c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Wed, 11 Mar 2026 18:07:11 +0100 Subject: [PATCH 12/28] Address some review comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index afce45d6ee6..f1c67e935e9 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -38,7 +38,7 @@ data in binary packages, in the form of variant properties. It provided the foundations by organizing the variant properties into namespaces. However, it did not specify how variant namespaces are governed, nor how to determine which variant properties are compatible with a particular -use system. This PEP aims to fill this gap. +system. This PEP aims to fill this gap. The PEP is specifically aiming to make variant wheels suitable for satisfying three use cases: @@ -100,11 +100,11 @@ rules apply, in order: said list, the tools MUST use it. Otherwise, proceed to step 4. 4. If no static list of compatible features and their values is - provided, the variant provider metadata MUST specify a list of - required Python packages that provide a variant provider plugin. The - tools MAY choose to vendor or reimplement plugin packages at their - leisure. If that is the case, they MUST obtain the list from their - implementation. Otherwise, proceed to step 5. + provided, the variant provider metadata MUST specify a Python package + that provides a variant provider plugin. The tools MAY choose to + vendor or reimplement plugin packages. If that is the case, they + SHOULD obtain the list from their implementation. Otherwise, proceed + to step 5. 5. The tools MAY provide list of trusted packages that are permitted to be used by default. They SHOULD provide a way for the user to trust @@ -262,28 +262,29 @@ High level design ''''''''''''''''' All variants published on a single index for a specific package version -MUST use the same provider for a given namespace. Attempting to load -more than one plugin for the same namespace in the same release version -MUST result in a fatal error. While multiple plugins for the same -namespace MAY exist across different packages, release versions or -indexes (such as when a plugin is forked due to being unmaintained), -they are mutually exclusive within any single release version on an -index. +MUST use the same provider for a given namespace. While multiple plugins +for the same namespace MAY exist across different packages, release +versions or indexes (such as when a plugin is forked due to being +unmaintained), they are mutually exclusive within any single release +version on an index. To make it easier to discover and install plugins, they SHOULD be published in the same indexes as the packages using them. In particular, packages published to PyPI MUST NOT rely on plugins that need to be installed from other indexes. -Except for namespaces reserved as part of this PEP, installable Python -packages MUST be provided for plugins. The entire dependency tree of -such a plugin MUST be installable using non-variant wheels, and variant -wheels MUST NOT be used. +Except for namespaces reserved as part of this PEP and variant providers +that include static lists of compatible features and their values as +part of their metadata, installable Python packages MUST be provided for +plugins. The entire dependency tree of such packages MUST be installable +using non-variant wheels, and variant wheels MUST NOT be used while +installing them. As noted in the `Providers`_ section, these plugins can also be -reimplemented by tools needing them. In the latter case, the resulting -reimplementation does not need to follow the API defined in this -section. +reimplemented by tools needing them. If that is the case, the API +defined in this section does not apply to these implementations. +However, the reimplementer is responsible for ensuring consistent +behavior with the published provider. A plugin implemented as Python package exposes callables that are called via: @@ -575,7 +576,7 @@ for example debug or experimental builds of packages. Installing provider plugins in isolated environments is recommended, as that permits tools to automatically deploy them without affecting the system packages. However, this is not a requirement to permit other -options. For example, build backends may prefer reusing the isolated +options. For example, build frontends may prefer reusing the isolated build environment for this. The ``requires`` and ``plugin-api`` keys follow the precedent of From ad2a58653c153bd90cd6e58b3e79a4d4fe8f1f53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Wed, 11 Mar 2026 18:37:24 +0100 Subject: [PATCH 13/28] Clarify what kind of tools, and what the algorithm's purpose is MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index f1c67e935e9..9398de5ff5f 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -82,8 +82,12 @@ namespaces, as required by :pep:`825`. The namespace ``abi_dependency`` is reserved for the `ABI Dependency Variant Provider <#abi-dependency-variant-provider-optional>`_. All other namespaces used MUST be defined in the `provider information`_ -dictionary in `variant metadata`_. For every namespace, the following -rules apply, in order: +dictionary in `variant metadata`_. + +Installers and other tools that need to determine whether a variant is +compatible with the system MUST follow the specified algorithm in order +to obtain the list of compatible features and feature values for every +namespace: 1. A variant provider MAY be enabled or disabled by default. The tools MUST provide a way to explicitly enable a provider, and MAY provide a From eaf7aad8a372f242ff7e67014b18da8c6065e9f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Thu, 12 Mar 2026 14:54:26 +0100 Subject: [PATCH 14/28] Address more feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index 9398de5ff5f..4b528875be5 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -97,11 +97,12 @@ namespace: 2. A variant provider's metadata MAY include a static list of compatible features and their values. If that is the case, the tools MUST use - the static lists. Otherwise, proceed to step 3. + the static lists exclusively. Otherwise, proceed to step 3. 3. The tools SHOULD implement a way for the user to provide a static list of compatible features and their values. If the user provides - said list, the tools MUST use it. Otherwise, proceed to step 4. + said list, the tools MUST use it exclusively. Otherwise, proceed to + step 4. 4. If no static list of compatible features and their values is provided, the variant provider metadata MUST specify a Python package @@ -126,8 +127,8 @@ Variant metadata ---------------- This PEP extends the metadata introduced in :pep:`825` with an -additional ``providers`` key. Therefore, the metadata has the following -structure: +additional ``providers`` key, described in the `Provider information`_ +subsection. Therefore, the metadata has the following structure: .. code:: text @@ -153,9 +154,10 @@ included in the Appendix of this PEP. The schema is available in Provider information '''''''''''''''''''' -``providers`` is a dictionary, the keys are namespaces, the values are +Variant metadata must feature a ``providers`` key. It corresponds to a +dictionary, in which the keys are namespaces, the values are dictionaries with provider information. It specifies how to install and -use variant providers. Its set of keys MUST match the set of values in +use variant providers. Its set of keys MUST match the set of values in the ``default-priorities.namespace`` list of variant metadata. The use of provider information is described in the `Providers`_ and From 334bd96d7c5fe1ab74be9a3f24ede89297113959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Thu, 12 Mar 2026 15:00:47 +0100 Subject: [PATCH 15/28] Try clarifying `plugin-api` defaults MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index 4b528875be5..a7e92567edc 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -178,10 +178,11 @@ information dictionary: install the provider plugin. If the dependency specifiers include environment markers, these are evaluated against the environment where the plugin is being installed, and the requirements for which the - markers evaluate to false are filtered out. At least one dependency + markers evaluate to false are filtered out. At least one dependency MUST remain present in every possible environment after filtering. - Additionally, if ``plugin-api`` is not specified, the first dependency - present after filtering MUST always evaluate to the same API endpoint. + Additionally, if ``plugin-api`` is not specified explicitly, the first + dependency that remains after filtering MUST always evaluate to the + same default ``plugin-api`` value. A provider information dictionary MAY additionally contain the following key: @@ -195,9 +196,10 @@ contain the following key: - ``plugin-api: str``: The API endpoint for the plugin. If it is specified, it MUST be an object reference as explained in the `API - endpoint`_ section. If it is missing, the package name from the first - dependency specifier in ``requires`` is used, after replacing all - ``-`` characters with ``_`` in the normalized package name. + endpoint`_ section. If it is not specified, a default value is + determined by normalizing the package name from the first dependency + specifier in ``requires`` and then replacing all ``-`` characters with + ``_`` in the normalized package name. It is invalid to specify ``requires`` or ``plugin-api`` if ``static-properties`` are present. From f9fe5aef2ff6ced17417ed493ec7670c6927d774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Thu, 28 May 2026 18:23:03 +0200 Subject: [PATCH 16/28] Try rewording the ultimate algorithm steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index a7e92567edc..df23d2b9d73 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -111,16 +111,18 @@ namespace: SHOULD obtain the list from their implementation. Otherwise, proceed to step 5. -5. The tools MAY provide list of trusted packages that are permitted to - be used by default. They SHOULD provide a way for the user to trust - additional packages. If the list of required provider packages - contains any untrusted package, the tools MUST assume that the list - of compatible features is empty and they MUST NOT install or use the - provider packages. Otherwise, proceed to step 6. - -6. The tools MUST install the specified provider packages and query them - via the `provider plugin API`_. The tools SHOULD use an isolated - virtual environment for that purpose. +5. The tools MAY consider some of the required provider plugin packages + (and their dependencies) to be trusted by default, and install them + automatically. They SHOULD provide a way for the user to permit + installing additional provider packages. They MUST NOT install or use + any provider packages that are not considered trusted. The tools + SHOULD use an isolated virtual environment while installing provider + plugin packages. + +6. The tools MUST query the trusted and installed provider packages + via the `provider plugin API`_ to obtain the list of compatible + features and feature values. For providers that were not trusted, + the tools MUST assume that the list of compatible features is empty. Variant metadata From 6579a6c8ec6f6b0867a036fc907754ceaa0eab92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Thu, 28 May 2026 18:25:20 +0200 Subject: [PATCH 17/28] Apply suggestions from code review Co-authored-by: Jonathan DEKHTIAR --- peps/pep-9999.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index df23d2b9d73..25893f97d29 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -156,7 +156,7 @@ included in the Appendix of this PEP. The schema is available in Provider information '''''''''''''''''''' -Variant metadata must feature a ``providers`` key. It corresponds to a +Variant metadata must include a ``providers`` key. It corresponds to a dictionary, in which the keys are namespaces, the values are dictionaries with provider information. It specifies how to install and use variant providers. Its set of keys MUST match the set of values in From 4cc2ac5ce92f78e58b1472a6234f421bd049078a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Thu, 28 May 2026 18:28:27 +0200 Subject: [PATCH 18/28] Reword namespace match MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index 25893f97d29..41027c2dbc0 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -159,8 +159,9 @@ Provider information Variant metadata must include a ``providers`` key. It corresponds to a dictionary, in which the keys are namespaces, the values are dictionaries with provider information. It specifies how to install and -use variant providers. Its set of keys MUST match the set of values in -the ``default-priorities.namespace`` list of variant metadata. +use variant providers. All the namespaces found in +``default-priorities.namespace`` MUST be listed in this dictionary, +and other keys MUST NOT be present. The use of provider information is described in the `Providers`_ and `Provider plugin API`_ sections. From 0ba6ef4582091117f2d0cce08e7eed7e70b15c89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Wed, 12 Aug 2026 15:02:43 +0200 Subject: [PATCH 19/28] Sync the schema to PEP 825 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 4 -- peps/pep-9999/variant-schema-0.2.0.json | 69 ++++--------------------- 2 files changed, 9 insertions(+), 64 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index 41027c2dbc0..dc3f4a55a89 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -223,10 +223,6 @@ in the following way: "default-priorities": { // MUST list all namespaces used. "namespace": ["x86_64", "aarch64", "blas_lapack"], - - // other fields same as in PEP 825 - "feature": {}, - "property": {} }, "providers": { diff --git a/peps/pep-9999/variant-schema-0.2.0.json b/peps/pep-9999/variant-schema-0.2.0.json index a1b1eec162e..d31cd5b37f1 100644 --- a/peps/pep-9999/variant-schema-0.2.0.json +++ b/peps/pep-9999/variant-schema-0.2.0.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://variants-schema.wheelnext.dev/peps/826/v0.2.0.json", + "$id": "https://variants-schema.wheelnext.dev/peps/9999/v0.2.0.json", "title": "Variant metadata, v0.2.0", "description": "The format for variant metadata (variant.json) and index-level metadata ({name}-{version}-variants.json)", "type": "object", @@ -22,54 +22,9 @@ }, "minItems": 1, "uniqueItems": true - }, - "feature": { - "description": "Default feature priorities (by namespace)", - "type": "object", - "patternProperties": { - "^[a-z0-9_]+$": { - "description": "The most preferred features", - "type": "array", - "items": { - "type": "string", - "pattern": "^[a-z0-9_]+$" - }, - "minItems": 0, - "uniqueItems": true - } - }, - "additionalProperties": false, - "uniqueItems": true - }, - "property": { - "description": "Default property priorities (by namespace)", - "type": "object", - "patternProperties": { - "^[a-z0-9_]+$": { - "description": "Default property priorities (by feature name)", - "type": "object", - "patternProperties": { - "^[a-z0-9_]+$": { - "description": "The most preferred feature values", - "type": "array", - "items": { - "type": "string", - "pattern": "^[a-z0-9_.]+$" - }, - "minItems": 0, - "uniqueItems": true - } - }, - "additionalProperties": false, - "uniqueItems": true - } - }, - "additionalProperties": false, - "uniqueItems": true } }, "additionalProperties": false, - "uniqueItems": true, "required": [ "namespace" ] @@ -115,34 +70,32 @@ "minItems": 0, "uniqueItems": true }, - "additionalProperties": false, - "uniqueItems": true + "additionalProperties": false } } }, "additionalProperties": false, - "uniqueItems": true, "anyOf": [ {"required": ["requires"]}, {"required": ["static-properties"]} ] } }, - "additionalProperties": false, - "uniqueItems": true + "additionalProperties": false }, "variants": { "description": "Mapping of variant labels to properties", "type": "object", "patternProperties": { - "^[a-z0-9_.]{1,16}$": { + "^[a-z0-9_.]+$": { "type": "object", "description": "Mapping of namespaces in a variant", "patternProperties": { - "^[a-z0-9_.]+$": { + "^[a-z0-9_]+$": { "description": "Mapping of feature names in a namespace", + "type": "object", "patternProperties": { - "^[a-z0-9_.]+$": { + "^[a-z0-9_]+$": { "description": "List of values for this variant feature", "type": "array", "items": { @@ -153,16 +106,13 @@ "uniqueItems": true } }, - "uniqueItems": true, "additionalProperties": false } }, - "uniqueItems": true, "additionalProperties": false } }, - "additionalProperties": false, - "uniqueItems": true + "additionalProperties": false } }, "required": [ @@ -171,6 +121,5 @@ "providers", "variants" ], - "additionalProperties": false, - "uniqueItems": true + "additionalProperties": false } From c10a013cef065a477123f07b2cb1ce03d1e03b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Wed, 12 Aug 2026 15:24:06 +0200 Subject: [PATCH 20/28] Add metadata consistency note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index dc3f4a55a89..d8ef6de3761 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -207,6 +207,10 @@ contain the following key: It is invalid to specify ``requires`` or ``plugin-api`` if ``static-properties`` are present. +For the metadata to be consistent, the same keys MUST always correspond +to the same values. When combining metadata, the resulting ``providers`` +dictionary MUST be a union of all the input dictionaries. + Example ''''''' From f2c79b5f3032482078ba563dc6557e6557382ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Wed, 12 Aug 2026 16:29:05 +0200 Subject: [PATCH 21/28] Fill the gaps in `requires` package naming rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Explicitly specify that the package name in dependency must be the same, rather than using an indirection to `plugin-api` rules. This is simple enough to inline it, and makes the spec easier to read. Technically, we could also say "normalized name" here, but I don't think we really need to allow different spellings of the same name here. Extend the rule to require it to match the first package name from the unfiltered list. This fills a corner case where the first dependency would always be filtered out: in which case the old rule would still be met but you'd have to be computing `plugin-api` from the filtered list every time. Now both approaches are valid, since they yield the same result. Also link package name normalization here, since it's the first reference to it. Signed-off-by: Michał Górny --- peps/pep-9999.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index d8ef6de3761..0024f3b5997 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -184,8 +184,8 @@ information dictionary: markers evaluate to false are filtered out. At least one dependency MUST remain present in every possible environment after filtering. Additionally, if ``plugin-api`` is not specified explicitly, the first - dependency that remains after filtering MUST always evaluate to the - same default ``plugin-api`` value. + dependency that remains after filtering MUST always reference the same + package name as the first dependency prior to filtering. A provider information dictionary MAY additionally contain the following key: @@ -200,9 +200,10 @@ contain the following key: - ``plugin-api: str``: The API endpoint for the plugin. If it is specified, it MUST be an object reference as explained in the `API endpoint`_ section. If it is not specified, a default value is - determined by normalizing the package name from the first dependency - specifier in ``requires`` and then replacing all ``-`` characters with - ``_`` in the normalized package name. + determined by + :ref:`normalizing the package name ` + from the first dependency specifier in ``requires`` and then replacing + all ``-`` characters with ``_`` in the normalized package name. It is invalid to specify ``requires`` or ``plugin-api`` if ``static-properties`` are present. From 667e92c2953255c7133f1b48728fbc2081824f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Wed, 12 Aug 2026 16:42:02 +0200 Subject: [PATCH 22/28] Simplify the "Python packages are required rule" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have just defined `static-properties`, so let's just reference it instead of trying to describe it in a mouthful. Signed-off-by: Michał Górny --- peps/pep-9999.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index 0024f3b5997..16d4257663a 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -286,11 +286,10 @@ particular, packages published to PyPI MUST NOT rely on plugins that need to be installed from other indexes. Except for namespaces reserved as part of this PEP and variant providers -that include static lists of compatible features and their values as -part of their metadata, installable Python packages MUST be provided for -plugins. The entire dependency tree of such packages MUST be installable -using non-variant wheels, and variant wheels MUST NOT be used while -installing them. +using the ``static-properties`` key, installable Python packages MUST be +provided for plugins. The entire dependency tree of such packages MUST +be installable using non-variant wheels, and variant wheels MUST NOT be +used while installing them. As noted in the `Providers`_ section, these plugins can also be reimplemented by tools needing them. If that is the case, the API From 838b16189581bfad5bd9f27fefcb915844306516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Wed, 12 Aug 2026 17:10:07 +0200 Subject: [PATCH 23/28] Split variant plugin discovery into a dedicated section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of listing it in passing as a minor point in "API endpoint", let's shortly describe what it is. Signed-off-by: Michał Górny --- peps/pep-9999.rst | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index 16d4257663a..840fda2e7d3 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -334,15 +334,35 @@ pseudocode: API endpoints are used in two contexts: -a. in the ``plugin-api`` key of variant metadata, either explicitly or - inferred from the package name in the ``requires`` key. This is the - primary method of using the plugin when building and installing - wheels. - -b. as the value of an installed entry point in the ``variant_plugins`` - group. The name of said entry point is insignificant. This is - OPTIONAL but RECOMMENDED, as it permits variant-related utilities to - discover variant plugins installed to the user's environment. +a. in the ``plugin-api`` key of `provider information`_ in variant + metadata, either explicitly or inferred from the package name in the + ``requires`` key. This is the primary method of using the plugin when + building and installing wheels. + +b. as the value of an installed entry point, as described in + `variant plugin discovery`_. + + +Variant plugin discovery +------------------------ + +The Python packages providing variant provider plugins SHOULD install +an entry point to facilitate discovery by variant-related utilities. +The entry point MUST be placed in the ``variant_plugins`` group, its +name is insignificant and its value MUST be the plugin's `API +endpoint`_. For example, a plugin can declare the following entry +point: + +.. code:: toml + + [project.entry-points.variant_plugins] + provider_variant_x86_64 = "provider_variant_x86_64" + +A tool that facilitates such discovery mechanism MUST collect all entry +points in the ``variant_plugins`` group and use their respective values +to determine the API endpoints to call. They MAY use the associated +package distribution metadata to determine which package provides a +given plugin. Variant feature config class From 7cc2e35cf04f4ecfd8c82d4398c0b856e3c2a1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Wed, 12 Aug 2026 17:15:27 +0200 Subject: [PATCH 24/28] De-emphasize plugin API description using protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's describe the API first, and only list the protocol as an equivalent description of the same thing. This reduces the risk that someone will assume that they actually need to implement this via protocols. Signed-off-by: Michał Górny --- peps/pep-9999.rst | 48 ++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index 840fda2e7d3..e5cbedf3825 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -371,7 +371,21 @@ Variant feature config class The variant feature config class is used as a return value in plugin API functions. It defines a single variant feature, along with a list of possible values. Depending on the context, the order of values MAY be -significant. It is defined using the following protocol: +significant. Its instance MUST provide the following properties or +attributes: + +- ``name: str`` specifying the feature name. + +- ``multi_value: bool`` specifying whether the feature is allowed to + have multiple corresponding values within a single variant wheel. If + it is ``False``, then it is an error to specify multiple values for + the feature. + +- ``values: list[str]`` specifying feature values. In contexts where the + order is significant, the values MUST be ordered from the most + preferred to the least preferred. + +Equivalently, it can be described using the following protocol: .. code:: python @@ -398,24 +412,20 @@ significant. It is defined using the following protocol: """List of values, possibly ordered from most preferred to least""" raise NotImplementedError -The instance MUST provide the following properties or attributes: - -- ``name: str`` specifying the feature name. - -- ``multi_value: bool`` specifying whether the feature is allowed to - have multiple corresponding values within a single variant wheel. If - it is ``False``, then it is an error to specify multiple values for - the feature. - -- ``values: list[str]`` specifying feature values. In contexts where the - order is significant, the values MUST be ordered from the most - preferred to the least preferred. - Plugin interface '''''''''''''''' -The plugin interface MUST follow the following protocol: +The plugin interface, as referenced by the `API endpoint`_, MUST provide +the following function: + +- ``get_supported_configs() -> list[VariantFeatureConfigType]`` that + returns a list of feature names and their values that are compatible + with the system the plugin is running on. The variant feature and + value lists MUST be ordered from the most preferred to the least + preferred. + +Equivalently, it can be described using the following protocol: .. code:: python @@ -430,14 +440,6 @@ The plugin interface MUST follow the following protocol: """Get ordered lists of compatible features and their values""" raise NotImplementedError -The plugin interface MUST provide the following function: - -- ``get_supported_configs() -> list[VariantFeatureConfigType]`` that - returns a list of feature names and their values that are compatible - with the system the plugin is running on. The variant feature and - value lists MUST be ordered from the most preferred to the least - preferred. - Example implementation '''''''''''''''''''''' From f91c11019df0c21b102605e3ef7782faacbc440e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Wed, 12 Aug 2026 17:18:41 +0200 Subject: [PATCH 25/28] Make `abi_dependency` namespace use clearer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than saying "built wheel variant property", which could be misinterpreted as not being allowed in index-level metadata, let's just say that it appears in "`variants` dictionary". Signed-off-by: Michał Górny --- peps/pep-9999.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index e5cbedf3825..b2259685b29 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -520,8 +520,8 @@ The variant namespace ``abi_dependency`` is reserved for expressing that different builds of the same version of a package are compatible with different versions or version ranges of a dependency. This namespace MUST NOT be listed in the `provider information`_ dictionary nor in the -``default-priorities.namespace`` dictionary, and can only appear in a -built wheel variant property. +``default-priorities.namespace`` dictionary, and can only appear in the +``variants`` dictionary. Within this namespace, zero or more properties can be used to express compatible dependency versions. For each property, the feature name MUST From 7755c8d0d91b8d63495c2325e841906118a65f50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Wed, 12 Aug 2026 17:27:52 +0200 Subject: [PATCH 26/28] Improve the rationale wording a bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index b2259685b29..8a46cae7213 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -581,11 +581,10 @@ advantages: multiple tools, and released to the public after proving the concept. At the same time, it is understood that installing additional Python -packages and running the code from them at install time introduces -additional attack vector (as discussed in `security implications`_). -For this reason, plugin packages are entirely opt-in, and a few -mechanisms are provided to improve the user experience without -compromising security: +packages and running the code from them introduces additional attack +vector (as discussed in `security implications`_). For this reason, +plugin packages are entirely opt-in, and a few mechanisms are provided +to improve the user experience without compromising security: - Users can provide static compatibility lists to avoid querying the providers. This also permits deploying packages for different systems @@ -593,10 +592,10 @@ compromising security: - Tools can maintain their own lists of trusted provider plugins that are enabled by default, or they can vendor or reimplement some - providers. This is entirely voluntary, as not to impose maintenance - effort on tool maintainers. At the same time, the ability to - reimplement providers avoids introducing a performance bottleneck on - tools that aren't written in Python. + providers. This is entirely voluntary, to avoid imposing an additional + maintenance burden on tool maintainers. At the same time, the ability + to reimplement providers avoids introducing a performance bottleneck + on tools that aren't written in Python. - Variant wheels can include static lists of compatible properties, to facilitate variants that do not need querying platform capabilities, @@ -608,15 +607,14 @@ for example debug or experimental builds of packages. Installing provider plugins in isolated environments is recommended, as that permits tools to automatically deploy them without affecting the -system packages. However, this is not a requirement to permit other -options. For example, build frontends may prefer reusing the isolated -build environment for this. +system packages. However, this is not a requirement. For example, build +frontends may prefer reusing the isolated build environment for this. The ``requires`` and ``plugin-api`` keys follow the precedent of ``build-system.requires`` and ``build-system.build-backend`` keys of -:pep:`517`. However, following the criticism of that design, -``plugin-api`` has been made optional and defaults to being inferred -from the package name. +:pep:`517`. However, the ``plugin-api`` key has been made optional and +defaults to being inferred from the package name, addressing a criticism +of the original design. The `ABI Dependency Variant Provider <#abi-dependency-variant-provider-optional>`_ is defined separately, as From a796ad6d3bbbbaf4e978247e592a6578b337d1eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Thu, 13 Aug 2026 13:41:51 +0200 Subject: [PATCH 27/28] Move the provider consistency requirement into rationale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index 8a46cae7213..e664df1a8b6 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -273,13 +273,6 @@ Provider plugin API High level design ''''''''''''''''' -All variants published on a single index for a specific package version -MUST use the same provider for a given namespace. While multiple plugins -for the same namespace MAY exist across different packages, release -versions or indexes (such as when a plugin is forked due to being -unmaintained), they are mutually exclusive within any single release -version on an index. - To make it easier to discover and install plugins, they SHOULD be published in the same indexes as the packages using them. In particular, packages published to PyPI MUST NOT rely on plugins that @@ -610,6 +603,14 @@ that permits tools to automatically deploy them without affecting the system packages. However, this is not a requirement. For example, build frontends may prefer reusing the isolated build environment for this. +Providers are defined as part of the variant metadata. From the metadata +consistency requirements, it follows that multiple variant wheels for a +single package version published on a single index must always use the +same provider for a given namespace. This requirement does not hold +across different packages, package versions or indexes. This permits +both adjusting dependency bounds and switching to different provider +plugins with a new package version. + The ``requires`` and ``plugin-api`` keys follow the precedent of ``build-system.requires`` and ``build-system.build-backend`` keys of :pep:`517`. However, the ``plugin-api`` key has been made optional and From 8a445adce2468f6bffbd4e1b4da446559214206d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Thu, 13 Aug 2026 13:45:07 +0200 Subject: [PATCH 28/28] Add rationale for entry points MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- peps/pep-9999.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/peps/pep-9999.rst b/peps/pep-9999.rst index e664df1a8b6..5391990d954 100644 --- a/peps/pep-9999.rst +++ b/peps/pep-9999.rst @@ -617,6 +617,11 @@ The ``requires`` and ``plugin-api`` keys follow the precedent of defaults to being inferred from the package name, addressing a criticism of the original design. +It is recommended for plugins to install entry points to facilitate +discovery when installed in development environments. This can be used +by variant-related tools, for example to aid debugging variant selection +or help configuring source trees. + The `ABI Dependency Variant Provider <#abi-dependency-variant-provider-optional>`_ is defined separately, as it needs to interact with the dependency resolver. To avoid adding