Reference doc consolidating findings on PEP 817 variant resolution, the variant provider protocol, and how ROCm device packages integrate. Written to stop re-deriving this from scratch every session.
Variant resolution has two distinct stages:
- Wheel selection: The variant provider reports system capabilities. The installer uses this to pick which variant wheel to install.
- Dep activation: The installed wheel's own
variant.jsonproperties driveRequires-Distmarker evaluation, pulling in conditional deps.
These are separate. The provider doesn't control deps — it only influences which wheel gets selected. The wheel's metadata controls the dep fan-out.
Providers implement variantlib.protocols.PluginType:
class AMDVariantPlugin:
namespace = "amd"
is_aot_plugin = False
@classmethod
def get_all_configs(cls) -> list[VariantFeatureConfig]:
# Static: every known GFX target
return [VariantFeatureConfig(
name="gfx_arch",
values=[t.name for t in ALL_TARGETS],
multi_value=True,
)]
@classmethod
def get_supported_configs(cls) -> list[VariantFeatureConfig]:
# Dynamic: detected GPUs on this system
targets = detect_gfx_targets()
return [VariantFeatureConfig(
name="gfx_arch",
values=[t.name for t in targets],
multi_value=True,
)] if targets else []Key points:
get_all_configs()is constant — same result on any system. Used for validation.get_supported_configs()is dynamic — probes the current system. Values ordered by preference (most preferred first).multi_value=Truemeans a single wheel can declare multiple values and matches if ANY declared value appears in the system's supported list.- Providers run in a subprocess (not in-process). uv spawns a Python process, communicates via JSON on stdin/stdout.
- Provider reports leaf targets only (e.g.,
gfx1151). The hierarchy fan-out to device packages is the wheel metadata's job.
PEP 817 introduces four new markers for use in Requires-Dist:
| Marker | Type | Example |
|---|---|---|
variant_namespaces |
set[str] |
"amd" in variant_namespaces |
variant_features |
set[str] |
"amd :: gfx_arch" in variant_features |
variant_properties |
set[str] |
"amd :: gfx_arch :: gfx1151" in variant_properties |
variant_label |
str |
variant_label == "rocm63" |
These are evaluated against the installed wheel's own variant.json,
not against the provider output. This is the mechanism that activates
the right device package dependencies.
torch-2.7.0-cp313-cp313-linux_x86_64-rocm_all.whl
^^^^^^^^^
variant label (1-16 chars)
{
"providers": {
"amd": {
"requires": ["rocm-bootstrap>=0.1.0"],
"plugin-api": "rocm_bootstrap.variant_provider:AMDVariantPlugin",
"install-time": true,
"enable-if": "platform_system == 'Linux'"
}
},
"variants": {
"rocm_all": {
"amd": {"gfx_arch": ["gfx942", "gfx1100", "gfx1151"]}
}
}
}Published at {name}-{version}-variants.json on the index. Aggregates
all variant entries so uv can select without downloading every wheel.
- One torch variant wheel covering all built ROCm arches.
- Per-target device wheels at up to 3 hierarchy levels (target, sub-family, family), version-locked 1:1 with the host torch version.
- Empty levels are omitted (no package published).
The kpack wheel rebuilder writes marker-conditioned deps for every
built target, using rocm_bootstrap.packaging_chain() to determine
the hierarchy and device_dist_name() for the package names:
Requires-Dist: torch-device-gfx942==2.7.0; "amd :: gfx_arch :: gfx942" in variant_properties
Requires-Dist: torch-device-gfx9-4==2.7.0; "amd :: gfx_arch :: gfx942" in variant_properties
Requires-Dist: torch-device-gfx9==2.7.0; "amd :: gfx_arch :: gfx942" in variant_properties
Requires-Dist: torch-device-gfx1100==2.7.0; "amd :: gfx_arch :: gfx1100" in variant_properties
Requires-Dist: torch-device-gfx11==2.7.0; "amd :: gfx_arch :: gfx1100" in variant_properties
Requires-Dist: torch-device-gfx1151==2.7.0; "amd :: gfx_arch :: gfx1151" in variant_properties
Requires-Dist: torch-device-gfx11==2.7.0; "amd :: gfx_arch :: gfx1151" in variant_properties
Notes:
torch-device-gfx11==2.7.0appears under both gfx1100 and gfx1151. pip/uv deduplicates during resolution.- Empty levels (e.g., gfx11-5 with no sub-family kernels) are simply omitted — no dep entry generated.
- Version is pinned
==because device packages are 1:1 locked with the host wheel version.
- uv fetches
torch-2.7.0-variants.jsonfrom the index. - uv installs
rocm-bootstrapinto an isolated env (perproviders.amd.requires). - uv invokes the provider subprocess → gets
gfx_arch=["gfx1151"]. - uv matches against variant candidates. The
rocm_allvariant declaresgfx_arch: ["gfx942", "gfx1100", "gfx1151"]. Sincegfx1151is in the list andmulti_value=True, it matches. - uv installs the selected torch wheel.
- uv evaluates
Requires-Distmarkers against the wheel's variant properties. Only deps conditioned ongfx1151activate. - uv installs
torch-device-gfx1151==2.7.0andtorch-device-gfx11==2.7.0.
For users on standard pip without variant support:
Provides-Extra: gfx942
Requires-Dist: torch-device-gfx942==2.7.0; extra == "gfx942"
Requires-Dist: torch-device-gfx9-4==2.7.0; extra == "gfx942"
Requires-Dist: torch-device-gfx9==2.7.0; extra == "gfx942"
Provides-Extra: gfx1151
Requires-Dist: torch-device-gfx1151==2.7.0; extra == "gfx1151"
Requires-Dist: torch-device-gfx11==2.7.0; extra == "gfx1151"
Then pip install torch[gfx1151] works without variant resolution.
Both mechanisms (variant markers and extras) can coexist in the same
METADATA. The variant markers are ignored by non-variant-aware
installers.
The kpack wheel rebuilder uses these APIs to generate all metadata:
from rocm_bootstrap import packaging_chain, device_dist_name
for target_name in built_targets:
chain = packaging_chain(target_name)
for bundle in chain:
dist_name = device_dist_name("torch-device", bundle)
if dist_name in published_packages:
# Variant marker dep:
# Requires-Dist: {dist_name}=={version}; "amd :: gfx_arch :: {target_name}" in variant_properties
# Extras dep:
# Requires-Dist: {dist_name}=={version}; extra == "{target_name}"-
Provider reports leaf targets only. The hierarchy fan-out is a packaging concern handled by the wheel rebuilder at build time.
-
xnack/ASAN not in package names. xnack+/- kernels go inside arch packages. ASAN is a parallel universe at the index/version level.
-
Empty hierarchy levels are skipped. If a sub-family has no kernels, no package is published and no dep is generated.
-
Device packages are version-locked 1:1 with the host wheel. The rebuilder knows the version from the fat wheel it's splitting and carries it forward to all device wheels and
==pins.
- PEP 817 — Wheel Variants: Beyond Platform Tags
- wheelnext/variantlib — reference implementation
- wheelnext/amd-variant-provider — current AMD provider (to be replaced by rocm-bootstrap)
- uv variant prototype PR #12203
- PyPI: rocm-bootstrap
rocm_bootstrap.variant_provider— our provider implementationrocm_bootstrap.packaging_chain()— hierarchy lookup for rebuilderrocm_bootstrap.device_dist_name()— device package name generation