
























[packages.variant_json]This PEP proposes variant wheels, an extension to
Binary distribution format that permits
building multiple variants of the same package while embedding
additional compatibility data. The specific properties are stored inside
the wheel, and expressed via a human-readable variant label in the
filename, which is then mapped to the actual properties via a separately
hosted JSON mapping. This aims to make {tool} install {package}
capable of selecting the most appropriate variant of packages where
additional compatibility dimensions such as GPU support need to be
accounted for.
This PEP proposes a protocol to record additional compatibility data in binary packages, to allow tools to pick the correct package to use in situations where Platform compatibility tags are insufficient. There are many cases where this is necessary, most notably in the case of scientific and machine learning (ML) libraries, where high performance requires extension code that is carefully tailored to the precise hardware available in the user’s environment. Well known examples of this include:
The problem space has been explored in greater detail in PEP 817.
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
A variant wheel is an extension of the wheel format, defined in Binary distribution format. It MUST specify a variant label in the filename, which makes it distinct from non-variant wheels. It MUST include a variant metadata file, which maps the variant label to zero or more variant properties.
Variant properties express the compatibility of binary packages with specific platforms, in addition to Platform compatibility tags. They follow a key-value format, where a key is called a variant feature. The keys are further grouped into independently governed variant namespaces. Hence, a variant feature consists of a namespace and a feature name, whereas a variant property consists of a namespace, a feature name and a feature value.
Variant properties are serialized into a structured 3-tuple of the following format:
{namespace} :: {feature_name} :: {feature_value}
The properties with which the wheel was built are stored within the wheel, in the variant metadata file. A variant wheel can specify multiple values corresponding to a variant feature. For the wheel to be considered compatible with a system, at least one value for every feature listed in its properties MUST be compatible with the system. A variant wheel with zero properties is always deemed compatible.
The namespace and feature name components MUST be non-empty and consist
only of 0-9, a-z and _ ASCII characters (^[a-z0-9_]+$).
The feature value MUST be non-empty and consist only of 0-9,
a-z, _ and . ASCII Characters (^[a-z0-9_.]+$).
The available properties and the rules governing their compatibility will be defined in a subsequent PEP.
Examples:
# the system must be compatible with all of the following x86_64 :: level :: v3 x86_64 :: avx512_bf16 :: on nvidia :: cuda_version_lower_bound :: 12.8 # it must also be compatible with at least one of the following nvidia :: sm_arch :: 120_real nvidia :: sm_arch :: 110_real
The wheel filename template originally defined by PEP 427 is changed to:
{distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}(-{variant label})?.whl
+++++++++++++++++++
The Python tag component MUST NOT start with a digit.
Variant wheels MUST include the variant label component. Conversely,
wheels without variant label are non-variant wheels. The variant label
MUST be non-empty and consist only of 0-9, a-z, _ and .
ASCII characters (^[0-9a-z_.]+$).
Every variant label MUST uniquely correspond to a specific set of variant properties, which MUST be the same for all wheels using the same label within a single package version.
The label null is reserved and always corresponds to the variant
with zero properties, called a null variant. This variant acts as a
fallback variant that is always compatible.
Examples:
numpy-2.3.2-cp313-cp313t-musllinux_1_2_x86_64.whlx86_64_v3:
numpy-2.3.2-cp313-cp313t-musllinux_1_2_x86_64-x86_64_v3.whlnumpy-2.3.2-cp313-cp313t-musllinux_1_2_x86_64-null.whlThe additional metadata specific to variant wheels is stored inside the
wheel, in *.dist-info/variant.json file, using the JSON format. This
PEP defines the following structure:
+- $schema
+- default-priorities
| +- namespace : list[str]
| +- feature
| +- {namespace} : list[str] = []
| +- property
| +- {namespace}
| +- {feature} : list[str] = []
+- variants
+- {variant_label}
+- {namespace}
+- {feature} : list[str] = []
This structure corresponds to the version 0.1.0 of the format. The
version number is stored as part of the schema URL. Whenever the format
changes, the version number must be incremented. Tools MUST assume that
all variant wheels using an unknown format version are unsupported.
The version numbers starting with zero are reserved for drafts and MUST
NOT be used in production. Once the proposal is complete, the latest
draft will be promoted to version 1.0.0.
The top-level keys are described in the subsequent sections.
The $schema key is the standard way of specifying the JSON schema used. Its value MUST be the URL of a JSON
schema corresponding to this specification, hosted on
packaging.python.org. The schema URL MUST include a version number,
and consequently every schema MUST describe the matching format version.
The schema can be used to verify the validity of the JSON file prior to
processing it, or after outputting it.
A proposed JSON schema for the current format version is included in the Appendix of this PEP. Subsequent PEPs changing the metadata format will include updated versions of the schema. The schema is available in Appendix: JSON Schema for Variant Metadata.
The default-priorities dictionary defines the ordering of variants.
The exact algorithm is described in the Variant ordering section.
The following key is REQUIRED:
namespace: list[str]: All variant namespaces used in variant
wheels for a given package version, ordered in decreasing priority.
This list MUST contain all namespaces used in variant properties.It MAY have the following OPTIONAL keys:
feature: dict[str, list[str]]: A dictionary with namespaces as
keys, and ordered list of corresponding feature names as values.
The feature names are ordered in decreasing priority. It is used to
override the default feature ordering.property: dict[str, dict[str, list[str]]]: A nested dictionary
with namespaces as first-level keys, feature names as second-level
keys and ordered lists of corresponding property values as
second-level values. The feature values are ordered in decreasing
priority. It is used to override the default value ordering.The variants dictionary provides a mapping from variant labels
to variant properties. In the variant wheel, it MUST contain the label
present in that wheel’s filename.
It has 3 levels. The first level keys are variant labels, the second level keys are namespaces, the third level are feature names, and the third level values are sets of feature values, converted to lists, sorted lexically.
{ // The schema URL will be replaced with the final URL on packaging.python.org "$schema": "https://variants-schema.wheelnext.dev/peps/825/v0.1.0.json", "default-priorities": { // REQUIRED: specifies that x86_64 CPU properties are more important than // aarch64 CPU properties (both are mutually exclusive, so the exact order // does not matter), and both are more important than specific BLAS/LAPACK // library: "namespace": ["x86_64", "aarch64", "blas_lapack"], // OPTIONAL: makes "library" the most important feature in "blas_lapack" // namespace "feature": { "blas_lapack": ["library"] }, // OPTIONAL: makes ["mkl", "openblas"] the most important values of // "blas_lapack :: library" feature "property": { "blas_lapack": { "library": ["mkl", "openblas"] } } }, "variants": { // REQUIRED: in variant.json, always a single entry, with the key // matching the variant label ("x86_64_v3_openblas") and the value // specifying its properties (the system must be compatible with both): // - blas_lapack :: library :: openblas // - x86_64 :: level :: v3 "x86_64_v3_openblas": { "blas_lapack": { "library": ["openblas"] }, "x86_64": { "level": ["v3"] } } } }
For every package version that includes at least one variant wheel,
there MUST exist a corresponding {name}-{version}-variants.json
file. The {name} and {version} placeholders correspond to the
package name and version, normalized according to the same rules as
wheel files, as found in the File name convention of
the Binary Distribution Format specification. The exact URL where the
file is hosted is insignificant, but a link to it MUST be present on all
index pages where the variant wheels are linked. It is presented in the
same simple repository format as source distribution and wheel links in
the index, including an (OPTIONAL) hash.
This file uses the same structure as variant metadata, except that
the variants object MUST list all variants available on the package
index for the package version in question. The tools MUST ensure that
the variant metadata across multiple variant wheels of the same package
version and the index-level metadata file is consistent. They MAY
require that keys other than variants have exactly the same values,
or they may carefully merge their values, provided that no conflicting
information is introduced, and the result is the same irrespective of
the order in which the wheels are processed.
This file SHOULD NOT be considered immutable and MAY be updated in a backward compatible way at any point (e.g. when adding a new variant).
Variant indexes MAY elect to either auto-generate the file from the uploaded variant wheels or allow the user to manually generate it themselves and upload it to the index.
The foo-1.2.3-variants.json corresponding to the package with two
wheel variants, one of them listed in the previous example, would look
like:
{ // The schema URL will be replaced with the final URL on packaging.python.org "$schema": "https://variants-schema.wheelnext.dev/peps/825/v0.1.0.json", "default-priorities": { // identical to above }, "variants": { // REQUIRED: entries for all wheel variants for the package version // if a null variant is present "null": {}, // "x86_64_v3_openblas" label corresponds to: // - blas_lapack :: library :: openblas // - x86_64 :: level :: v3 "x86_64_v3_openblas": { "blas_lapack": { "library": ["openblas"] }, "x86_64": { "level": ["v3"] } }, // "x86_64_v4_mkl" label corresponds to: // - blas_lapack :: library :: mkl // - x86_64 :: level :: v4 "x86_64_v4_mkl": { "blas_lapack": { "library": ["mkl"] }, "x86_64": { "level": ["v4"] } } } }
To determine which variant wheel to install when multiple wheels are compatible, variants MUST be totally ordered by their variant properties.
For the purpose of ordering, variant properties are grouped into features, and features into namespaces. For every namespace, the tool MUST obtain an ordered list of compatible features, and for every feature, an ordered list of compatible values. The method of obtaining these lists will be defined in a subsequent PEP.
The default ordering MUST be performed equivalent to the following algorithm:
default-priorities.namespace key from index-level metadata.
This is namespace_order in the example.default-priorities.feature.{namespace}
key.After this step, a list of ordered feature names is available for
every namespace. This is feature_order in the example.
default-priorities.property.{namespace}.{feature_name} key.After this step, a list of ordered property values is available for
every feature. This is value_order in the example.
Variant.best_value_properties() method in the example.property_key()
function in the example.Variant.sorted_properties() method in the example.Variant.__lt__().After this process, the variant wheels are sorted from the most preferred to the least preferred. The algorithm sorts the null variant after all the other variants. The non-variant wheel MUST be ordered after the null variant. Multiple wheels with the same variant property set (and multiple non-variant wheels) MUST then be ordered according to their platform compatibility tags.
The tools MAY provide options to override the default ordering, for example by specifying a preference for specific namespaces, features or properties. The tools MAY also provide options to exclude specific variants, or to select a particular variant.
Alternatively, the sort algorithm for variant wheels could be described using the following pseudocode. For simplicity, this code does not account for non-variant wheels or tags.
from typing import Self def get_compatible_feature_names(namespace: str) -> list[str]: """Get an ordered list of compatible features""" ... def get_compatible_feature_values(namespace: str, feature_name: str) -> list[str]: """Get an ordered list of compatible values""" ... # default-priorities dict from index-level metadata default_priorities = { "namespace": [...], # : list[str] "feature": {...}, # : dict[str, list[str]] "property": {...}, # : dict[str, dict[str, list[str]]] } # 1. Construct the ordered list of namespaces. namespace_order = default_priorities["namespace"] feature_order = {} value_order = {} for namespace in namespace_order: # 2. Construct the ordered lists of feature names. feature_order[namespace] = default_priorities["feature"].get(namespace, []) for feature_name in get_compatible_feature_names(namespace): if feature_name not in feature_order[namespace]: feature_order[namespace].append(feature_name) value_order[namespace] = {} for feature_name in feature_order[namespace]: # 3. Construct the ordered lists of feature values. value_order[namespace][feature_name] = ( default_priorities["property"].get(namespace, {}).get(feature_name, []) ) for feature_value in get_compatible_feature_values(namespace, feature_name): if feature_value not in value_order[namespace][feature_name]: value_order[namespace][feature_name].append(feature_value) def best_value_property(namespace: str, feature_name: str, feature_values: str) -> str: """Helper function to determine the best value for given feature""" for best_value in value_order[namespace][feature_name]: if best_value in feature_values: return best_value assert False, "No feature value supported, wheel should have been filtered out" def property_key(prop: tuple[str, str, str]) -> tuple[int, int, int]: """Construct a sort key for variant property (akin to step 5.)""" namespace, feature_name, feature_value = prop return ( namespace_order.index(namespace), feature_order[namespace].index(feature_name), value_order[namespace][feature_name].index(feature_value), ) class Variant: """Example class exposing properties of a variant wheel""" label: str # {namespace: {feature_name: [feature_values]}}, as in variant.json properties: dict[str, dict[str, list[str]]] def best_value_properties(self: Self) -> list[tuple[str, str, str]]: """Determine the most preferred values for every feature, step 4.""" return [ ( namespace, feature_name, best_value_property(namespace, feature_name, feature_values), ) for namespace, features in self.properties.items() for feature_name, feature_values in features.items() ] def sorted_properties(self: Self) -> list[tuple[str, str, str]]: """Sort the list of features with their best values (step 6.)""" return sorted(self.best_value_properties(), key=property_key) def __lt__(self: Self, other: Self) -> bool: """Variant comparison function for sorting (part of step 7.)""" self_properties = self.sorted_properties() other_properties = other.sorted_properties() # Proceed from the first to the last common sort best-value property. # If any of them are different, the variant with better property wins. for self_prop, other_prop in zip(self_properties, other_properties): if self_prop != other_prop: return property_key(self_prop) < property_key(other_prop) # If the best-value properties of one variant are a subset of another, # the one with more properties wins. if len(self_properties) != len(other_properties): return len(self_properties) > len(other_properties) # If two variants have exactly the same properties, fall back to # sorting on variant label (they must be unique). return self.label < other.label # A list of variants to sort. variants: list[Variant] = [...] # 7. Order variants by comparing their sorted properties # (see Variant.__lt__()) variants.sort()
Four new environment markers are introduced in dependency specifications:
variant_namespaces corresponding to the set of namespaces of all
the variant properties that the wheel variant was built for.variant_features corresponding to the set of
namespace :: feature pairs of all the variant properties that the
wheel variant was built for.variant_properties corresponding to the set of
namespace :: feature :: value tuples of all the variant
properties that the wheel variant was built for.variant_label corresponding to the exact variant label that the
wheel was built with. For the non-variant wheel, it is an empty
string.The markers evaluating to sets of strings MUST be matched via the in
or not in operator, e.g.:
# satisfied by any "foo :: * :: *" property dep1; "foo" in variant_namespaces # satisfied by any "foo :: bar :: *" property dep2; "foo :: bar" in variant_features # satisfied only by "foo :: bar :: baz" property dep3; "foo :: bar :: baz" in variant_properties
The variant_label marker is a plain string:
# satisfied by the variant "foobar" dep4; variant_label == "foobar" # satisfied by any wheel other other than the null variant # (including the non-variant wheel) dep5; variant_label != "null" # satisfied by the non-variant wheel dep6; variant_label == ""
Implementations MUST ignore differences in whitespace while matching the features and properties.
Variant marker expressions MUST be evaluated against the variant properties stored in the wheel being installed.
Variant wheels can be listed in pylock.toml file in the same manner
as wheels with different Platform compatibility tags: either all variant
(and non-variant) wheels can be listed, or a subset of them.
A new [packages.variants-json] subtable is added to the file. It
MUST inline the contents of the index-level metadata file, converting
the JSON structure into the respective TOML types. The $schema key
MUST be preserved to facilitate versioning. The tools MAY remove keys
that are not relevant to variant wheels present in pylock.toml.
If variant wheels are listed, the tool SHOULD resolve variants to select the best wheel file.
The proposed text for pylock.toml Specification follows:
[packages.variant_json]lock-version = "1.0" created-by = "uv" requires-python = ">=3.14" [[packages]] name = "numpy" version = "2.3.4" index = "https://pypi.anaconda.org/mgorny/simple" wheels = [ { url = "https://pypi.anaconda.org/mgorny/simple/numpy/2.3.4/numpy-2.3.4-cp314-cp314-linux_x86_64-openblas.whl", hashes = {} }, { url = "https://pypi.anaconda.org/mgorny/simple/numpy/2.3.4/numpy-2.3.4-cp314-cp314-linux_x86_64-x86_64_v4_mkl.whl", hashes = {} }, { url = "https://pypi.anaconda.org/mgorny/simple/numpy/2.3.4/numpy-2.3.4-cp314-cp314-macosx_13_0_x86_64-accelerate.whl", hashes = {} }, { url = "https://pypi.anaconda.org/mgorny/simple/numpy/2.3.4/numpy-2.3.4-cp314-cp314-macosx_13_0_x86_64-openblas.whl", hashes = {} }, ] [packages.variant_json] "$schema" = "https://variants-schema.wheelnext.dev/peps/825/v0.1.0.json" [packages.variant_json.default-priorities] namespace = [ "x86_64", "aarch64", "blas_lapack" ] [packages.variant_json.variants] null = { } x86_64_v3_openblas = { "blas_lapack" = { "library" = ["openblas"]}, "x86_64" = { "level" = ["v3"]} } x86_64_v4_mkl = { "blas_lapack" = { "library" = ["mkl"]}, "x86_64" = { "level" = ["v4"]} }
When asked to install a version of a package from an index, the proposed behavior would be to:
{name}-{version}-variants.json. If this
file is missing, assume all variant wheels are incompatible and
proceed as with non-variant wheels.Note that steps 4. through 8. are introduced specifically for variant wheels. The remaining steps correspond to the current installer behavior.
When asked to install a local wheel file, the proposed behavior would be to:
*.dist-info/variant.json inside
the wheel file.Variant wheels are uploaded to an index just like regular wheels.
There are two possible approaches to publishing the index-level
{name}-{version}-variants.json file for every package version:
it can either be prepared and uploaded by the user, or it can be
generated automatically by the index.
If the index is responsible for generating the file, it should use some mechanism to defer publishing it until the release is fully uploaded (for example, PEP 694).
To generate the {name}-{version}-variants.json file:
*.dist-info/variant.json file.*.dist-info/variant.json files into the existing data:variants dictionary are merged togetherdefault-priorities.namespace list can be replaced if the new
value starts with the old valuedefault-priorities.feature and default-priorities.value
keys can be added if they were not present in the previous
default-priorities.namespace valueVariant wheels use structured variant properties to express multidimensional wheel compatibility matrices. For example, it permits expressing that a single variant requires certain CPU and GPU features independently. It can express both AND-style dependencies (such as different CPU instruction sets) and OR-style dependencies (such as different GPUs supported by a single package).
The specification does not impose any formal limits on the number of properties expressed, and specifically accounts for the possibility of property sets being very long (for example, a long list of GPUs or CPU extension sets). To avoid wheel filenames becoming very long, the property lists are stored inside the wheel and mapped to a short label that is intended to be human-readable.
To facilitate variant selection while installing from remote index, the variant metadata is mirrored in a JSON file published on the index. This enables installers to obtain variant property mapping without having to fetch individual wheels.
Since JSON format does not feature a set type, sets are represented as sorted lists. Sorting ensures that tools can safely use equality comparison over dictionaries.
The variant ordering algorithm has been proposed with the assumption that variant properties take precedence over Platform compatibility tags, as they are primarily used to express user preferences. This accounts for possible divergence of platform tags, e.g. because a CUDA variant may require a different minimal libc version, in which case the selection should be driven by the desired CUDA preference rather than incidental platform tag difference.
While the provision of variant properties is deferred to a future PEP to keep the specification easier to comprehend, a baseline assumption is made that the compatible properties will be provided in specific order corresponding to their preference, much like Platform compatibility tags conventionally are. The variant metadata provides the ability to override this order at package level. However, namespaces are unordered by design (e.g. we will not decide upfront which GPU vendors take precedence) and therefore they always need to be ordered by the package maintainer.
A concept of null variant is introduced that is distinct from non-variant wheels to facilitate a transition period. This variant is always supported by tools implementing this PEP, and takes precedence over non-variant wheel. It can therefore be used to provide a distinct fallback for the cases of no other variant being supported and variant wheels being unsupported altogether. For example, PyTorch could provide a much smaller null variant that is used when no GPU is supported, and a fallback non-variant wheel built for the default CUDA version.
pylock.toml integration inlines the variant metadata to keep the
file standalone. This can avoid the additional network call that would
be required to fetch the file, and avoids having to pin to a specific
hash that could cause problems if the file changed on the index, either
due to the variant metadata being updated or being generated in a way
that does not guarantee stable bytewise output.
Variant wheels add an additional variant label component to the wheel filename. A complete filename verification step should reject such wheels:
numpy-2.3.2-1-cp313-cp313t-musllinux_1_2_x86_64-x86_64_v3.whl
^^^^^^^^^^
numpy-2.3.2-cp313-cp313t-musllinux_1_2_x86_64-x86_64_v3.whl
^^^^^
Currently, no Python tags start with a digit. To guarantee unambiguity, the specification enforces that going forward. Tools commonly used to install wheels at the time of writing implemented a verification algorithm of that kind, making it possible to publish variant wheels on an index alongside non-variant wheels without risk of them being installed accidentally.
Tools that do not perform full filename verification will consume some or all variant wheels as regular wheels. This may cause unexpected behavior or breakage if the tool in question needs to specially account for variant wheels.
The libraries for processing wheel files and their consumers will need to be updated to handle the new filename component and possibly the new metadata. For example, there is an open discussion in packaging project how to adapt the parse_wheel_filename() function.
The addition of the variant label increases the filename length. On platforms with a low total path length limit such as Windows, long filenames are a concern. However, given that the name and version components are already unrestricted, we do not set a specific limit in this PEP. Others, such as PyPI, may set a limit for total filename length.
Aside from this explicit incompatibility, the specification makes
minimal and non-intrusive changes to the binary package format. The
variant metadata is stored in a separate file in the .dist-info
directory. Tools that are not directly concerned with variants need only
to update their filename verification algorithm (if there is one) and
preserve the contents of said directory.
If the new environment markers are used in wheel dependencies, these wheels will be incompatible with existing tools. For example, upon meeting these markers in a dependency from an index, pip will backtrack and use an older dependency version (if possible). This is a general problem with the design of environment markers, and not specific to wheel variants. It is possible to work around it by partially evaluating environment markers at build time, and removing the markers or dependencies specific to variant wheels from the non-variant wheel.
The presence of variant wheels may lead to some of the variants being subject to less scrutiny than others, and as such becoming easier attack targets. Particularly, once variant wheel support becomes commonplace, the non-variant wheels for some packages may be only consumed by users with outdated tools. However, such attacks assume that the package publishing workflow is already compromised, in which case more plausible attack vectors are available, for example via modifying compiled extensions.
This PEP is oriented at tool authors. Its changes will be integrated into Binary distribution format and other PyPA specifications. Teaching variants to end users will be covered in a subsequent PEP, as user experience details are addressed.
The variantlib project contains a reference implementation of a complete variant wheel solution. It is compliant with this PEP, but also goes beyond it, providing example solutions to open issues.
A client for installing variant wheels is implemented in a uv branch.
The specification proposes that variant labels are arbitrary, and variant properties are mapped to them via a variant metadata file rather than expressed directly in them. While it could be technically possible to create variant labels from variant properties, this would either require permitting very long filenames that will cause issues with some platforms, or imposing arbitrary limits on variant property counts, making the specification less suitable for addressing multidimensional compatibility matrices.
An alternative approach was to use a hash of variant properties. While such an approach is technically valid and can provide short unique labels for arbitrarily large variant property sets, it makes the labels opaque and therefore difficult to read or reason about.
The specification adds the variant label as a separate component,
therefore breaking compatibility with existing tools. It could be
technically possible to preserve partial compatibility by appending it
to one of the Platform compatibility tags instead, in which case
installers would reject the wheel based on platform (or Python
interpreter) incompatibility, while other tools could still use it.
However, the authors decided it safer to break the backwards
compatibility. Additionally, reusing tags posed a potential risk of
wheel labels being incorrectly combined with compressed tag sets. For
example, a manylinux_2_27_x86_64.manylinux_2_28_x86_64+x86_64_v3 tag
would be incorrectly deemed compatible because of the
manylinux_2_27_x86_64 part.
The following problems are deferred to subsequent PEPs:
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.
variants dictionary are sets,
and that they ought to be sorted when serializing.pylock.toml integration to inline variant metadata
rather than storing a URL and a hash.This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。