PEP 9999 – Wheel Variants: Providers
- Author:
- Jonathan Dekhtiar <jonathan at dekhtiar.com>, Michał Górny <mgorny at quansight.com>, Konstantin Schütze <konstin at mailbox.org>, Ralf Gommers <ralf.gommers at gmail.com>, Andrey Talman <atalman at meta.com>, Charlie Marsh <charlie at astral.sh>, Michael Sarahan <msarahan at gmail.com>, Eli Uriegas <eliuriegas at meta.com>, Barry Warsaw <barry at python.org>, Donald Stufft <donald at stufft.io>, Andy R. Terrel <andy.terrel at gmail.com>
- Discussions-To:
- Pending
- Status:
- Draft
- Type:
- Standards Track
- Topic:
- Packaging
- Requires:
- 825
- Created:
- 22-Sep-2026
- Post-History:
- Pending
Abstract
This PEP extends PEP 825 by defining how variant properties are governed and how their compatibility is determined. This is done either via static feature and feature value compatibility lists specified in the variant metadata, or by querying opt-in plugins that are Python packages, but can also be vendored or reimplemented by the tools. A static list can also be provided in place of querying the plugins. Additionally, an ABI Dependency Variant Provider is defined to facilitate builds of the same package against different dependency versions.
Motivation
PEP 825 defines a protocol for recording additional compatibility data for binary packages, in the form of variant properties, but does not define how to obtain the matching information for the system. This PEP defines how to determine which variant properties are compatible with a particular system and how variant namespaces are governed.
This PEP introduces three different ways of defining and obtaining variant properties for three different use cases:
- 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.
- 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 users with the ability to explicitly select a non-default variant.
- Variants that express compatibility with different dependency versions, particularly aiming to express Application Binary Interface (ABI) compatibility between extension modules. The goal is to enable matching variants against other packages, where one source distribution can be narrowed to specific versions of a dependency during build.
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 providers for all namespaces used MUST be defined in the provider information dictionary in variant metadata. This PEP is concerned only with consuming said dictionary; the process of building variant wheels will be covered in a subsequent PEP.
Each of these providers either defines a static list of compatible features and their values, specifies a list of requirements referring to Python packages or is one of the builtin providers.
Builtin providers are implemented within the installer to handle checks that the plugin API cannot support. For example, the ABI Dependency Variant Provider checks whether a wheel built against a particular version of a dependency (e.g. PyTorch) is compatible with the version selected during dependency resolution. This requires access to the installer’s dependency resolver.
For Python package providers (also called provider plugins), an API endpoint is either explicitly specified or inferred from the first dependency string. The canonical way of querying the list of compatible features and their values is then to install the specified packages and call this endpoint. The combination of dependency strings and the API endpoint are used to uniquely identify the provider, while the respective version constraints can be used to indicate the minimum versions emitting variant properties used in the package.
For security reasons, none of these packages are installed or used by default. Tools can provide a way to securely use a subset of provider packages, for example through vendoring, a reimplementation or an embedded lockfile. To aid in this, a shared list of such packages along with trusted implementations will be maintained; the details of said list and its governance will be provided in a subsequent PEP. Using provider plugins that are not trusted, such as the actual package from an index fulfilling the requirement, requires explicit user opt-in. This ensures only trusted code is executed during installation, while still supporting the evolution of providers and establishing clear governance over each namespace.
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:
- The tool SHOULD implement a way for the user to provide a static list of compatible features and their values for a particular provider. Standardizing this format is left to a future PEP. If the user provided said list, the tool MUST use it exclusively. Otherwise, proceed to step 2.
- The tool MUST read the
optionalkey from the provider information to determine whether the variant provider is disabled by default. It SHOULD provide a way to explicitly enable a provider, and MAY provide a way to disable one. If a provider is disabled, the list of compatible features is empty. Otherwise, proceed to step 3. - The tool MUST read the
builtinkey from the provider information dictionary. If it is present, the tool MUST use the internal provider implementation if supported, or assume that no features are compatible if not. Otherwise, proceed to step 4. - The tool MUST read the
static-propertieskey from the provider information dictionary. If it is present, the tool MUST use the static lists of properties therein exclusively. Otherwise, proceed to step 5. - If no static list of compatible features and their values is
provided, the tool MUST read the
requiresandplugin-apikeys in the provider information dictionary to determine the packages providing the compatibility information. If the tool provides a secure implementation of the specific providers, it SHOULD obtain the list from their implementation. Otherwise, proceed to step 6. - The tool SHOULD provide a way for the user to permit installing specific provider packages. It MUST NOT install or use any provider packages that are not considered trusted. It SHOULD use an isolated virtual environment while installing provider plugin packages.
- The tool 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, it MUST assume that the list of compatible features is empty.
For every unique provider, the tool MUST obtain the list of compatible features and their values only once throughout the install session and use it across all the packages being installed.
Variant metadata
This PEP extends the metadata introduced in PEP 825 with an
additional providers key, described in the Provider information
subsection.
(root)
|
+- $schema
+- default-priorities
+- variants
+- providers
+- {namespace}
+- builtin : str
+- feature-order : list[str]
+- optional : bool = False
+- plugin-api : str
+- 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
Appendix: JSON Schema for Variant Metadata.
Provider information
Variant metadata in a variant wheel 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. 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.
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, 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 thefeature-orderkey.requires: list[str]: A list of one or more package 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, ifplugin-apiis not specified explicitly, the first dependency that remains after filtering MUST always reference the same package name as the first dependency prior to filtering.builtin: str: Specifies the unique name identifying the builtin provider specified in a PEP. If it is provided, the provider is identified by the value and MUST behave as specified in the PEP defining it. If the tool does not recognize the particular provider, it MUST assume that its list of compatible features is empty.
A provider information dictionary MAY additionally contain the following key:
optional: bool: Whether the provider is optional. Defaults tofalse. If it istrue, the provider is disabled by default and needs to be enabled explicitly.
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 endpoint section. If it is not specified, a default value is determined by normalizing the package name from the first dependency specifier inrequiresand then replacing all-characters with_in the normalized package name.
If the static-properties key is present, the dictionary MAY
additionally contain the following key:
feature-order: list[str]: The list controlling the order of compatible features. When present, all keys found instatic-propertiesMUST be listed here. Entries not found in thestatic-propertiesdictionary MUST NOT appear in this list.
To reiterate, in addition to the optional key, the following key
combinations are valid:
requires, with optionalplugin-apistatic-properties, with optionalfeature-orderbuiltin
The providers dictionary is copied into index-level metadata. 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
Both the variant metadata example and the index-level metadata example from PEP 825 can be extended in the following way:
{
// The schema URL will be replaced with the final URL on packaging.python.org
"$schema": "https://variants-schema.wheelnext.dev/peps/9999/v0.2.0.json",
"default-priorities": {
// MUST list all namespaces used.
"namespace": ["x86_64", "aarch64", "blas_lapack", "abi"],
},
"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"]
// "plugin-api" is OPTIONAL here. It is inferred from "requires":
// "plugin-api": "provider_variant_aarch64"
},
"abi": {
"builtin": "abi_dependency"
},
"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"]
},
},
"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
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 variant providers using the static-properties and
builtin keys, 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
defined in this section does not apply to these implementations.
However, the reimplementer is responsible for ensuring consistent
behavior with the published provider. The reimplementations and vendored
providers MUST be matched to the published providers using the
requires and plugin-api keys, rather than namespaces.
A plugin implemented as Python package exposes callables that are called via:
{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.
Any errors occurring while installing or executing the plugin API, including the plugin code terminating the calling process, MUST result in tools aborting the install process.
API endpoint
The location of the plugin code is called an “API endpoint”, and it is expressed using the object reference notation following the Entry points specification:
{import_path}(:{object_path})?
An API endpoint specification is equivalent to the following Python pseudocode:
import {import_path}
if "{object_path}":
plugin = {import_path}.{object_path}
else:
plugin = {import_path}
API endpoints are used in two contexts:
- in the
plugin-apikey of provider information in variant metadata, either explicitly or inferred from the package name in therequireskey. This is the primary method of using the plugin when building and installing wheels. - 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
being the recommended namespace name, and its value being the plugin’s
API endpoint. For example, a plugin can declare the following entry
point:
[project.entry-points.variant_plugins]
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 entry point
name to recommend the namespace to use, and the associated package
distribution metadata to determine which package provides a given
plugin.
For example, with provider-variant-x86-64==0.0.3 package installing
the entry point shown above, variantlib update-pyproject-toml --add
x86_64 would add the following metadata to pyproject.toml:
[variant.default-priorities]
namespace = ["x86_64"]
[variant.providers.x86_64]
requires = ["provider-variant-x86-64 >=0.0.3"]
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. Its instance MUST provide the following properties or attributes:
name: strspecifying the feature name.multi_value: boolspecifying whether the feature is allowed to have multiple corresponding values within a single entry in thevariantsdictionary. If it isFalse, 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. This list MUST NOT be empty.
Equivalently, it can be described using the following protocol:
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
Plugin interface
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:
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
Example implementation
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
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 []
configs = [
VariantFeatureConfig(
name="min_version",
# [current, current - 1, ..., 1]
values=[str(x) for x in range(current_version, 0, -1)],
multi_value=False,
),
]
# 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
supported_gpus = [x for x in _ALL_GPUS if _is_gpu_available(x)]
if supported_gpus:
configs.append(
VariantFeatureConfig(
name="gpu",
values=supported_gpus,
multi_value=True,
)
)
return configs
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.
Builtin providers
Some compatibility checks require integration with a tool’s internals,
such as its dependency resolver, that the plugin API does not expose.
Builtin providers handle these checks directly within the tool. They
are defined in PEPs, and their identifiers are used in the builtin
key in the provider information dictionary.
All builtin providers are OPTIONAL. Tools that choose to implement them MUST follow the PEP implementing them. Tools that do not recognize a particular builtin provider MUST treat the variants using it as incompatible, and SHOULD inform users when such wheels are skipped.
The following builtin variant providers are defined:
| Identifier | Provider |
|---|---|
abi_dependency |
ABI Dependency Variant Provider |
Subsequent PEPs may define additional builtin providers.
ABI Dependency Variant Provider
The ABI Dependency Variant Provider is used to express that different
builds of the same version of a package are compatible with different
versions or version ranges of a dependency. Its identifier is
abi_dependency. The ordering between features and feature values is
undefined, and the respective namespace should be listed after all the
ordering-significant namespaces in the default-priorities.namespace
list. Package maintainers SHOULD NOT be publishing variant wheels where
two different ABI dependency property sets could be compatible with
the same system.
Within this namespace, zero or more properties can be used to express compatible dependency versions. For each property, the feature name MUST be the name of the dependency and the value MUST be a valid release segment of a public version identifier, as defined by the Version specifiers specification. Both the dependency name and version MUST be normalized according to the same rules as wheel files, as found in the File name convention of the Binary Distribution Format specification.
The feature version 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.:
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 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, 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, 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. 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
defaults to being inferred from the package name, reducing boilerplate.
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 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. At the same time, provisions were made to permit creating further builtin providers in the future, and to keep the provider information consistent between all types of providers.
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:
- Publishing a version of a variant provider plugin or one of its dependencies with malicious code.
- Introducing a malicious variant provider plugin in an existing package metadata.
While such attacks can already be done to the package’s dependencies, it is prudent not to add another attack surface.
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 by default. Users are often unaware of this risk. Securing build dependencies is non-trivial and not commonly done. More commonly, users opt-out of source distribution builds altogether instead.
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 proposes that tools provide a selected set of trusted plugins, only requiring opt-in for uncommon 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. Some packages will be able to simplify their installation instructions to a single command covering the different hardware accelerators.
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.
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
- 22-Sep-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-ifandinstall-timekeys have been removed from provider information, as they were deemed redundant. - The
static-propertiestable has been moved into provider information, and made mutually exclusive with therequireskey. - The
feature-orderkey has been added to facilitate the static property ordering after the removal ofdefault-priorities.featurefrom PEP 825. - The
valuesmember of the variant feature config class is now required to be non-empty. - The entry point name is now used to provide the recommended namespace for a provider.
- The feature names and values in the ABI Dependency Variant Provider are now normalized according to the wheel normalization rules, to match the restrictions in PEP 825.
- A provision has been added that a consistent list of compatible feature names and values from a single provider must be used throughout the install session.
- The ABI Dependency Variant Provider now requires an explicit
entry in the
default-priorities.namespaceandproviderskeys, and it was generalized into one of the possible builtin providers.
Appendices
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.