PEP 825 – 휠 변형: 패키지 형식
- 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>
- PEP-Delegate:
- Paul Moore <p.f.moore at gmail.com>
- Discussions-To:
- Discourse thread
- Status:
- Draft
- Type:
- Standards Track
- Topic:
- Packaging
- Created:
- 17-Feb-2026
- Post-History:
- 17-Feb-2026
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
이 PEP는 Binary distribution format의 확장인 변형 휠(variant wheel)의 데이터 형식을 제공합니다. 이 확장을 사용하면 추가 호환성 데이터를 포함하면서 같은 패키지의 여러 변형을 빌드할 수 있습니다. 이 데이터는 휠 내부에 저장되며, 파일 이름에서는 사람이 읽을 수 있는 변형 레이블로 표현됩니다.
휠이 색인에 호스팅되는 경우에는 최적화를 위해 이 데이터를 별도의 JSON 파일로도 추가로 제공합니다. 이후 변형 휠의 나머지 측면을 정의하는 PEP들이 추가로 나올 예정입니다. 이 명세의 최종 목표는 GPU 지원과 같은 추가 호환성 차원을 고려해야 하는 경우에 {tool} install {package}가 가장 적합한 패키지 변형을 선택할 수 있도록 하는 것입니다.
동기
이 PEP는 바이너리 패키지에 추가 호환성 데이터를 기록하는 프로토콜을 제안합니다. 이를 통해 Platform compatibility tags만으로는 불충분한 상황에서도 도구가 사용할 올바른 패키지를 고를 수 있습니다. 이러한 기능이 필요한 경우는 많으며, 특히 과학 및 머신 러닝(ML) 라이브러리에서 두드러집니다. 이러한 라이브러리는 높은 성능을 내기 위해 사용자 환경에서 사용할 수 있는 하드웨어에 정확히 맞춰 신중하게 조정된 확장 코드가 필요합니다. 잘 알려진 예는 다음과 같습니다.
- 사용자의 GPU 하드웨어와 드라이버에 의존하는 PyTorch 및 기타 ML 도구
- 서로 다른 선형 대수 라이브러리와 링크될 수 있는 SciPy 같은 과학 라이브러리
- 서로 다른 OpenMP 런타임과 링크될 수 있는 XGBoost 같은 라이브러리
- AVX2나 AVX-512처럼 특정 CPU 명령어 집합을 사용할 수 있을 때 쓸 수 있는 성능 향상 빌드를 배포하는 라이브러리
이 문제 영역은 PEP 817에서 더 자세히 다루었습니다.
명세
정의
이 문서에 나오는 핵심 단어 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL”은 RFC 2119에 설명된 대로 해석해야 합니다.
구현 요구 사항
이 명세는 변형 휠, 변형 메타데이터 파일, pylock.toml 파일처럼 이 명세에서 정의하거나 변경하는 여러 파일 형식을 생성하는 도구의 관점에서 작성되었습니다. 형식 정의의 맥락에서 “MUST”라는 단어는 데이터가 이 명세에 따라 유효하다고 간주되려면 해당 요구 사항을 반드시 충족해야 함을 구체적으로 나타냅니다.
이 명세에 따른 데이터 형식을 생성하는 도구는 항상 이 명세의 요구 사항을 충족하는 파일을 생성해야 합니다(MUST).
이 명세의 데이터 형식을 사용하는 도구는 데이터가 이러한 요구 사항을 충족하는지 검증하지 않아도 됩니다. 도구는 명세 형식을 충족하지 않는다고 스스로 확인한 데이터에 의존하지 않아야 합니다(SHOULD NOT). 적절한 대응 방식은 도구의 역할에 따라 다르며, wheel 파일의 유효하지 않은 코어 메타데이터에 대한 대응과 같은 패턴을 따릅니다. 업로드를 받는 패키지 색인처럼 유효하지 않은 데이터가 생태계에 들어오는 시점에 이를 거부할 수 있는 위치에 있는 도구는 그렇게 해야 합니다(SHOULD). 설치 프로그램이 의존성을 해석하는 경우처럼 이후 단계에서 이를 마주치는 도구는 즉시 실패하기보다는 우아하게 성능을 낮추는 쪽을 택해야 합니다(SHOULD). 예를 들어 해당 변형 휠을 무시하고 나머지 휠 중에서 선택할 수 있습니다.
변형 휠
변형 휠은 Binary distribution format에 정의된 휠 형식의 확장입니다. 변형 휠은 파일 이름에 Variant label을 지정해야 하며(MUST), 이를 통해 변형이 아닌 휠과 구별됩니다. 변형 휠은 Variant metadata 파일을 포함해야 하며(MUST), 이 파일은 변형 레이블을 0개 이상의 Variant properties에 매핑합니다.
변형 속성
변형 속성은 Platform compatibility tags에 더하여, 바이너리 패키지가 특정 플랫폼과 호환되는지를 나타냅니다. 변형 속성은 키-값 형식을 따르며, 여기서 키를 변형 기능이라고 부릅니다. 키는 다시 독립적으로 관리되는 변형 네임스페이스로 묶입니다. 따라서 변형 기능은 네임스페이스와 기능 이름으로 구성되는 반면, 변형 속성은 네임스페이스, 기능 이름, 기능 값으로 구성됩니다.
변형 속성은 다음 형식의 구조화된 3-튜플로 직렬화됩니다:
{namespace} :: {feature_name} :: {feature_value}
휠을 빌드할 때 사용된 속성은 휠 내부의 Variant metadata 파일에 저장됩니다. 변형 휠은 하나의 변형 기능에 대응하는 값을 여러 개 지정할 수 있습니다. 휠이 시스템과 호환되는 것으로 간주되려면, 휠의 속성에 나열된 모든 기능 각각에 대해 적어도 하나의 값이 해당 시스템과 반드시 호환되어야 합니다. 속성이 하나도 없는 변형 휠은 항상 호환되는 것으로 간주됩니다.
네임스페이스와 기능 이름 구성 요소는 반드시 비어 있지 않아야 하며, 0-9, a-z, _ ASCII 문자로만 구성되어야 합니다(^[a-z0-9_]+$). 기능 값은 반드시 비어 있지 않아야 하며, 0-9, a-z, _, . ASCII 문자로만 구성되어야 합니다(^[a-z0-9_.]+$).
사용 가능한 속성과 그 호환성을 규정하는 규칙은 후속 PEP에서 정의할 예정입니다.
예시:
# 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
변형 레이블
원래 PEP 427에서 정의한 휠 파일 이름 템플릿은 다음과 같이 변경됩니다:
{distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}(-{variant label})?.whl
+++++++++++++++++++
Python 태그 구성 요소는 숫자로 시작해서는 안 됩니다.
변형 휠은 변형 레이블 구성 요소를 반드시 포함해야 합니다. 반대로, 변형 레이블이 없는 휠은 비변형 휠입니다. 변형 레이블은 반드시 비어 있지 않아야 하며, 0-9, a-z, _, . ASCII 문자로만 구성되어야 합니다(^[0-9a-z_.]+$).
모든 변형 레이블은 특정한 변형 속성 집합 하나에 반드시 고유하게 대응해야 하며, 이 집합은 단일 패키지 버전 안에서 같은 레이블을 사용하는 모든 휠에 대해 동일해야 합니다.
레이블 null은 예약되어 있으며, 항상 속성이 하나도 없는 변형에 대응합니다. 이를 널 변형이라고 부릅니다. 이 변형은 항상 호환되는 대체(fallback) 변형 역할을 합니다.
예시:
- 비변형 휠:
numpy-2.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl - 변형 레이블이
x86_64_v3인 휠:numpy-2.3.2-cp313-cp313t-musllinux_1_2_x86_64-x86_64_v3.whl - 널 변형:
numpy-2.3.2-cp313-cp313t-musllinux_1_2_x86_64-null.whl
변형 메타데이터
변형 휠에 특화된 추가 메타데이터는 휠 내부의 *.dist-info/variant.json 파일에 JSON 형식으로 저장됩니다. 이 PEP는 다음 구조를 정의합니다:
+- $schema
+- default-priorities
| +- namespace : list[str]
+- variants
+- {variant_label}
+- {namespace}
+- {feature} : list[str] = []
이 구조는 형식의 0.1.1 버전에 해당합니다. 버전 번호는 Schema URL의 일부로 저장됩니다. 버전 번호는 의미적 버전 관리(semantic versioning)를 따릅니다.
0으로 시작하는 번호는 초안용으로 예약되어 있으며, 프로덕션 환경에서 사용해서는 안 됩니다(MUST NOT). 도구는 이러한 버전에 대해 어떠한 호환성도 가정해서는 안 됩니다(MUST NOT). 제안이 완성되면 최신 초안이 1.0.0 버전으로 승격됩니다.
명세에 하위 호환되지 않는 변경이 이루어지는 경우, 메이저 버전 번호를 반드시 증가시켜야 하며(MUST), 나머지 버전 구성 요소는 반드시 0으로 설정해야 합니다(MUST). 도구는 지원하지 않는 메이저 버전 번호를 가진 메타데이터를 반드시 거부해야 합니다(MUST). 이러한 변경을 도입하는 명세 갱신에서는 하위 호환성 및 전환에 관한 추가 고려 사항을 제시할 수 있습니다.
명세에 하위 호환되는 변경이 이루어지는 경우, 마이너 버전 번호를 반드시 증가시켜야 하며(MUST), 패치 번호는 반드시 0으로 설정해야 합니다(MUST). 변형 메타데이터를 소비하기만 하는 설치 프로그램 및 기타 도구는 메이저 버전이 지원되는 한, 지원하는 최신 마이너 버전 번호보다 더 새로운 마이너 버전 번호를 가진 메타데이터도 받아들여야 합니다(SHOULD). 변형 메타데이터를 출력하는 도구는 명시적으로 지원하지 않는 버전을 출력해서는 안 됩니다(MUST NOT).
최상위 키와 각 키의 범위 및 일관성 요구 사항은 이어지는 절에서 설명합니다.
스키마
$schema 키는 사용하는 JSON 스키마를 지정하는 표준 방법입니다. 그 값은 반드시 이 명세에 대응하는 JSON 스키마의 URL이어야 하며, 이 스키마는 packaging.python.org에서 호스팅됩니다(MUST). 스키마 URL에는 반드시 버전 번호가 포함되어야 하며(MUST), 따라서 모든 스키마는 반드시 그에 대응하는 형식 버전을 기술해야 합니다(MUST). 스키마는 JSON 파일을 처리하기 전이나 출력한 후에 그 유효성을 검증하는 데 사용할 수 있습니다.
현재 형식 버전에 대해 제안된 JSON 스키마가 이 PEP의 부록에 포함되어 있습니다. 메타데이터 형식을 변경하는 후속 PEP에는 갱신된 버전의 스키마가 포함됩니다. 스키마는 부록: 변형 메타데이터를 위한 JSON 스키마에서 확인할 수 있습니다.
기본 우선순위
default-priorities 딕셔너리는 변형 순서 결정에 사용되는 네임스페이스의 순서를 정의합니다. 정확한 알고리즘은 Variant ordering 절에서 설명합니다.
다음 키는 필수입니다(REQUIRED):
namespace: list[str]: 특정 패키지 버전의 변형 휠에서 사용되는 모든 변형 네임스페이스로, 우선순위가 높은 것부터 내림차순으로 정렬합니다. 이 목록에는 변형 속성에 사용되는 모든 네임스페이스가 반드시 포함되어야 하며(MUST), 목록이 비어 있어서는 안 됩니다(MUST NOT). 즉, 변형 휠을 제공하는 패키지 버전은 반드시 하나 이상의 변형 네임스페이스를 사용해야 합니다(MUST).
기본 우선순위는 프로젝트 범위에서 정의됩니다. 서로 다른 휠에서 이 값은 동일하거나 한쪽이 다른 쪽의 확장이어야 합니다(SHOULD). 즉, 추가 네임스페이스를 뒤에 덧붙일 수 있습니다(MAY). 더 긴 목록이 더 짧은 목록의 요소들로 같은 순서대로 시작하면, 메타데이터가 일관된 것으로 간주합니다. 이 경우 메타데이터를 결합하면 반드시 더 긴 목록이 사용되어야 합니다(MUST).
변형
variants 딕셔너리는 변형 레이블에서 변형 속성으로의 매핑을 제공합니다. 개별 변형 휠에서 이 딕셔너리의 범위는 해당 휠로 한정되며, 반드시 정확히 하나의 항목만 포함해야 합니다. 그 항목의 키는 해당 휠의 파일 이름에 있는 변형 레이블입니다.
이 딕셔너리는 3단계로 구성됩니다. 첫 번째 단계의 키는 변형 레이블이고, 두 번째 단계의 키는 네임스페이스이며, 세 번째 단계의 키는 기능 이름입니다. 세 번째 단계의 값은 기능 값의 집합이며, 리스트로 변환되어 사전순으로 정렬됩니다.
메타데이터가 일관되려면 같은 키는 반드시 항상 같은 값에 대응해야 합니다. 메타데이터를 결합할 때, 결과로 얻는 variants 딕셔너리는 반드시 모든 입력 딕셔너리의 합집합이어야 합니다.
예시
{
// The schema URL will be replaced with the final URL on packaging.python.org
"$schema": "https://variants-schema.wheelnext.dev/peps/825/v0.1.1.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"],
},
"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"]
}
}
}
}
색인 수준 메타데이터
변형 휠을 하나 이상 포함하는 패키지 버전이 색인에 호스팅될 때에는, 그에 대응하는 {name}-{version}-variants.json 파일도 반드시 함께 호스팅되어야 합니다. 이 파일의 목적은 의존성 해석 중에 여러 변형 휠을 가져올 필요를 없애 변형 메타데이터 조회를 최적화하는 것입니다. {name}과 {version} 자리 표시자는 패키지 이름과 버전에 해당하며, 바이너리 배포 형식(Binary Distribution Format) 명세의 File name convention에 나와 있는 wheel 파일과 같은 규칙에 따라 정규화됩니다.
파일이 호스팅되는 정확한 URL은 중요하지 않지만, 변형 휠이 포함된 모든 응답에서 이 URL을 반드시 제공해야 합니다. 이 파일은 Simple repository API의 파일 규칙을 따라야 합니다. 다만 색인이 제공하는 선택적 메타데이터 속성(core-metadata, dist-info-metadata, requires-python, yanked 등)은 이 파일에는 의미가 없습니다. 색인은 반드시 이 속성들을 생략해야 하며, 도구는 반드시 이 속성들을 무시해야 합니다.
이 파일은 Variant metadata와 같은 구조를 사용합니다. 다만 variants 객체의 범위는 색인 전체이며, 해당 패키지 버전에 대해 패키지 색인에서 사용할 수 있는 모든 변형을 반드시 나열해야 합니다. 이 파일은 Metadata consistency에 명시된 대로 개별 휠의 Variant metadata와 반드시 일관되어야 합니다.
클라이언트가 색인 수준 메타데이터 파일에 나열되지 않은 레이블을 가진 변형 휠을 발견하면, 이러한 휠을 무시하거나 해당 휠에서 변형 메타데이터를 직접 가져오는 방법 중 하나를 선택할 수 있습니다.
이 파일은 불변으로 간주하지 않아야 하며, 언제든지 하위 호환성을 유지하는 방식으로 갱신할 수 있습니다(예: 새 변형을 추가할 때).
변형 색인은 업로드된 변형 휠로부터 이 파일을 자동 생성하거나, 사용자가 직접 파일을 생성해 색인에 업로드하도록 허용하는 방법 중 하나를 선택할 수 있습니다.
같은 {name}-{version}-variants.json 파일을 다른 (도구별) 휠 모음에서 변형 휠과 함께 게시할 수도 있습니다. 흔히 사용되는 --find-links 옵션이 가리키는 디렉터리를 그 예로 들 수 있습니다.
휠 변형이 두 개 있고 그중 하나가 앞의 예에 나와 있는 패키지에 대응하는 foo-1.2.3-variants.json은 다음과 같습니다:
{
// The schema URL will be replaced with the final URL on packaging.python.org
"$schema": "https://variants-schema.wheelnext.dev/peps/825/v0.1.1.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"]
}
}
}
}
메타데이터 일관성
한 패키지 버전의 개별 변형 휠에 담긴 Variant metadata와, 게시된 경우의 Index-level metadata 파일은 모두 같은 릴리스를 기술하므로 서로 일치해야 합니다. 각 키를 정의하는 절에 전체 내용이 명시되어 있는 요구 사항을 한곳에 모으면 다음과 같습니다:
default-priorities.namespace: 두 리스트는 반드시 동일하거나, 더 긴 리스트가 더 짧은 리스트의 요소들로 같은 순서로 시작해야 합니다. 이들을 결합하면 반드시 더 긴 리스트가 나와야 합니다.variants: 같은 변형 레이블은 반드시 항상 같은 속성 집합에 매핑되어야 합니다. 이들을 결합하면 반드시 딕셔너리들의 합집합이 나와야 합니다.
두 규칙은 모두 대칭적이므로, 이 규칙을 만족하는 메타데이터를 결합하면 입력을 처리하는 순서와 관계없이 같은 결과를 얻습니다.
이 요구 사항을 충족하는 것은 해당 패키지 버전 게시자의 책임입니다. 데이터는 프로젝트마다 단일 출처에서 비롯되고 빌드 시점에 휠로 복사되므로, 한 릴리스의 휠들이 서로 다른 입력으로 빌드되지 않는 한 이 요구 사항은 구조적으로 충족됩니다.
변형 메타데이터를 사용하는 도구는 이 요구 사항이 충족된다고 가정해도 되며(MAY), 이를 검증할 필요는 없습니다. 도구가 이 요구 사항이 충족되지 않음을 실제로 확인한 경우에는 Implementation requirements에 기술된 대응이 적용됩니다.
사용자가 같은 패키지의 휠을 둘 이상의 공개된 출처에서 가져오는 경우, 어떤 게시자도 다른 출처와의 일관성을 보장할 수 있는 위치에 있지 않습니다. 이 경우 결합되는 출처들이 서로 일관되도록 보장하는 것은 사용자의 책임입니다. 도구는 출처 간의 불일치를 감지하거나 해소할 필요가 없습니다. 대신 도구가 합리적으로 할 수 있는 일은 Installing wheels from multiple sources (non-normative)에서 다룹니다.
변형 순서
이 명세는 변형 메타데이터의 존재 여부에 따라 서로 다른 휠 사이의 순서를 정의합니다.
순서를 정하기 위해 모든 후보 변형 휠에 대해 결합된 변형 메타데이터를 반드시 확보해야 합니다(MUST). 이 메타데이터는 Index-level metadata 파일에서 가져오거나, 개별 휠에서 가져와 Variant metadata 절에 명시된 대로 결합할 수 있습니다. 두 출처는 일관되어야 하므로 같은 결과를 내며, 인덱스 수준 메타데이터 파일에서 데이터를 얻는 편이 훨씬 비용이 적으므로 도구는 이 파일을 사용할 수 있는 경우 이를 우선해야 합니다(SHOULD).
적격한 모든 변형 휠의 변형 속성은 기능별로 묶이고, 기능은 다시 네임스페이스별로 묶입니다. 도구는 각 네임스페이스에 대해 호환되는 기능 목록을, 각 기능에 대해 호환되는 값 목록을 반드시 확보해야 합니다(MUST). 이 목록을 얻는 방법은 후속 PEP에서 정의할 예정입니다. 이 목록의 항목들은 특정한 순서로 제공되며, 이 순서는 변형 휠의 순서에 영향을 줍니다.
패키지 이름, 버전, 빌드 번호의 특정 조합에 해당하는 호환 휠들은 반드시 변형 레이블별로 묶어야 하며(MUST), 비변형 휠들은 반드시 별도의 그룹으로 구성해야 합니다(MUST). 그런 다음 변형 휠 그룹들은 반드시 다음 알고리즘에 따라 정렬해야 합니다(MUST).
- 결합된 변형 메타데이터에서
default-priorities.namespace키의 값을 복사하여 네임스페이스의 순서 목록을 구성합니다. 예제에서는namespace_order가 이에 해당합니다. - 각 네임스페이스에 대해, 앞서 확보한 호환 기능 이름의 순서 목록을 가져옵니다. 예제에서는
feature_order가 이에 해당합니다. - 각 기능에 대해, 앞서 확보한 호환 값의 순서 목록을 가져옵니다. 예제에서는
value_order가 이에 해당합니다. - 각 그룹에 대해, 그 그룹에 해당하는 변형 속성에 들어 있는 모든 변형 기능마다 가장 선호되는 값을 결정합니다. 이는 값들 가운데 순서가 정해진 속성 값 목록에서 위치가 가장 앞선 값을 찾는 방식으로 이루어집니다. 이 단계를 마치면 모든 변형에 대해 기능과 각 기능의 최선 값으로 이루어진 목록을 얻게 됩니다. 예제에서는
VariantWheel.best_value_properties()메서드가 이 작업을 수행합니다. - 이전 단계에서 구성한 목록의 각 항목에 대해, 해당 항목의 네임스페이스, 기능 이름, 최선 기능 값이 각각의 순서 목록에서 차지하는 인덱스로 이루어진 3-튜플을 정렬 키로 구성합니다. 예제에서는
property_key()함수가 이 작업을 수행합니다. - 각 그룹에 대해, 4단계에서 구성한 목록을 5단계에서 구성한 정렬 키를 사용하여 오름차순으로 정렬합니다. 예제에서는
VariantWheel.sorted_properties()메서드가 이 작업을 수행합니다. - 그룹의 순서를 정하려면 6단계에서 얻은 각 그룹의 정렬된 리스트를 비교합니다. 첫 번째 위치의 정렬 키가 서로 다르면 키가 더 낮은 그룹이 앞에 정렬됩니다. 같으면 두 번째 위치의 키를 비교하며, 우열이 가려지거나 어느 한 그룹의 리스트가 소진될 때까지 이 과정을 반복합니다. 후자의 경우 키가 더 많은 그룹이 앞에 정렬됩니다. 대체 수단으로, 두 그룹의 키 개수가 같으면 변형 레이블의 사전순 오름차순으로 정렬됩니다. 이는 예시 알고리즘의 마지막 단계에서 수행되며, 비교 함수는
VariantWheel.__lt__()로 구현되어 있습니다.
널 변형 휠 그룹은 변형 속성이 없으므로 알고리즘은 이를 마지막에 정렬합니다. 비변형 휠 그룹은 반드시 다른 모든 그룹 뒤에 배치해야 합니다(MUST).
그다음 각 그룹 안에서 휠은 반드시 플랫폼 호환성 태그에 따라 정렬해야 합니다. 이 과정을 거치면 변형 휠은 가장 선호되는 것부터 가장 덜 선호되는 것 순으로 정렬됩니다.
도구는 기본 순서를 재정의하는 옵션을 제공할 수 있으며, 예를 들어 특정 네임스페이스, 기능 또는 속성에 대한 선호를 지정하도록 할 수 있습니다. 도구는 특정 배리언트를 제외하거나 특정 배리언트를 선택하는 옵션을 제공할 수도 있습니다. 이러한 옵션은 호환된다고 판정된 휠에 대해 동작하므로 그 집합의 순서를 바꾸거나 범위를 좁힐 수는 있지만, 대상 시스템이 지원하지 않는 속성을 가진 휠이 선택되도록 해서는 안 됩니다. 설치 대상 시스템이 아닌 다른 시스템을 위한 변형 휠을 설치하는 일은 그 대신 어떤 속성을 지원되는 것으로 간주할지를 재정의하는 문제이며, 이는 이 PEP의 Out of scope에 해당합니다.
또는 변형 휠의 정렬 알고리즘을 다음 의사 코드로 설명할 수도 있습니다. 단순화를 위해 이 코드는 비변형 휠이나 그 이후의 플랫폼 호환성 태그에 따른 정렬은 고려하지 않습니다.
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 combined variant metadata
default_priorities = {
"namespace": [...], # : list[str]
}
# 1. Obtain the ordered list of namespaces from the variant metadata.
namespace_order = default_priorities["namespace"]
# 2. Obtain the ordered lists of features.
feature_order = {
namespace: get_compatible_feature_names(namespace)
for namespace in namespace_order
}
# 3. Obtain the ordered lists of feature values.
value_order = {
namespace: {
feature_name: get_compatible_feature_values(namespace, feature_name)
for feature_name in feature_order[namespace]
} for namespace in namespace_order
}
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 VariantWheel:
"""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 variant wheels to sort.
variant_wheels: list[VariantWheel] = [...]
# 7. Order variant wheels by comparing their sorted properties
# (see VariantWheel.__lt__())
variant_wheels.sort()
환경 마커
의존성 명세에 새로운 환경 마커 네 가지가 도입됩니다. Dependency specifiers 명세에 정의된 마커와 달리, 이 마커의 값은 모든 휠에서 같지 않습니다. 이 값은 처리 중인 휠이 빌드된 대상 변형 속성으로 범위가 한정됩니다. 이 값들은 반드시 Evaluating variant markers에 설명된 대로 구해야 합니다. 이 마커들은 다음과 같습니다.
variant_label: 처리 중인 휠의 정확한 배리언트 레이블을 나타내는 문자열입니다. 비변형 휠의 경우 빈 문자열입니다.variant_properties: 배리언트 레이블에 해당하는 속성 중 대상 시스템과 호환되는 모든 속성의namespace :: feature :: value튜플 집합입니다. 비변형 휠의 경우 빈 집합입니다.variant_features:variant_properties에 있는 속성에 해당하는 모든namespace :: feature쌍의 집합입니다.variant_namespaces:variant_properties에 있는 모든 속성의 모든 네임스페이스 집합입니다.
variant_label은 String 필드이고, variant_properties, variant_features, variant_namespaces는 Set of String 필드입니다. 각 필드에 사용할 수 있는 연산자와 그 의미는 Dependency specifiers에서 해당 필드 타입에 대해 정의한 것과 같습니다.
구현은 기능과 속성을 매칭할 때 :: 구분자 주변의 공백 차이를 반드시 무시해야 합니다.
배리언트 마커 평가
변형 마커는 반드시 의존성 지정자에서만 사용해야 하며, 휠 선택에 관여해서는 안 됩니다. 이 마커들은 이미 선택된 휠의 개별 의존성 지정자를 적용할지 결정합니다. 이 마커들은 반드시 Variant ordering에 설명된 대로 변형 휠 선택이 이루어진 후에만 평가해야 합니다.
이 마커들의 값은 반드시 다음과 같이 결정해야 합니다:
variant_label은 선택된 휠의 파일 이름에 나타나는 해당 휠의 변형 레이블입니다.- Variant metadata의
variants딕셔너리가 그 레이블에 매핑하는 속성들을 가져와, 나열된 모든 기능 값에 대해namespace :: feature :: value삼중항으로 확장합니다. 일관성 요구 사항에 따라 두 메타데이터가 서로 일치하는 것이 보장되므로, 이를 위해 휠 자체에 포함된 Variant metadata와 Index-level metadata 중 어느 것을 사용해도 됩니다. - 확장된 삼중항 집합은 변형 휠 선택 과정에서 이미 결정된 대로, 대상 시스템이 지원하는 변형 속성만 남도록 필터링됩니다.
variant_properties가 그 결과입니다. 휠은 대상 시스템이 그 휠을 지원하는 경우에만 선택될 수 있고, 이를 위해서는 휠이 선언하는 모든 기능에 대해 지원되는 값이 최소 하나 있어야 하므로, 이 단계에서 기능 하나가 통째로 제거되는 일은 없습니다. variant_features와variant_namespaces는variant_properties에서 도출됩니다.
비변형 휠의 경우 variant_label은 빈 문자열이고, 집합 값을 갖는 세 마커는 빈 집합입니다. 이러한 휠에 대해 변형 마커를 평가하는 데는 변형 메타데이터가 필요하지 않습니다.
예시
다음 의존성 지정자들은 네 가지 마커와 각 마커에 사용할 수 있는 연산자를 보여 줍니다:
# satisfied by the variant "foobar"
dep1; variant_label == "foobar"
# satisfied by any wheel other than the null variant
# (including the non-variant wheel)
dep2; variant_label != "null"
# satisfied by the non-variant wheel
dep3; variant_label == ""
# satisfied by any "foo :: * :: *" property
dep4; "foo" in variant_namespaces
# satisfied by any "foo :: bar :: *" property
dep5; "foo :: bar" in variant_features
# satisfied only by "foo :: bar :: baz" property
dep6; "foo :: bar :: baz" in variant_properties
# equivalent
dep7; "foo::bar::baz" in variant_properties
3단계의 필터링은 변형 기능에 여러 값이 나열되어 있고 대상 시스템이 그중 하나만 지원하면 되는 경우에만 드러납니다(Variant properties 참조). 속성에 다음 두 가지가 모두 포함된 휠을 생각해 봅시다:
nvidia :: sm_arch :: 120_real
nvidia :: sm_arch :: 110_real
두 아키텍처를 모두 제공하는 시스템에서는 variant_properties에 두 속성이 모두 포함됩니다. 전자만 제공하는 시스템에서는 nvidia :: sm_arch :: 120_real만 포함되므로, nvidia :: sm_arch :: 110_real을 검사하는 의존성 지정자는 충족되지 않습니다.
pylock.toml과의 통합
변형 휠은 서로 다른 플랫폼 호환성 태그를 가진 휠과 같은 방식으로 pylock.toml 파일에 나열할 수 있습니다. 즉, 모든 변형 (및 비변형) 휠을 나열할 수도 있고 그 일부만 나열할 수도 있습니다.
파일에 새로운 [packages.variants-json] 하위 테이블이 추가됩니다. 이 하위 테이블에는 반드시 Index-level metadata 파일과 같은 형식에 따라 JSON 구조를 해당 TOML 타입으로 변환하여, 결합된 변형 메타데이터를 인라인으로 넣어야 합니다. 버전 관리를 쉽게 하기 위해 $schema 키는 반드시 유지해야 합니다. 도구는 해당 패키지에 나열된 휠에 나타나지 않는 레이블을 가진 variants 딕셔너리 항목을 제거할 수 있으며, 그 결과 default-priorities.namespace에서 더 이상 사용되지 않게 되는 네임스페이스도 함께 제거할 수 있습니다. Evaluating variant markers에는 선택된 레이블에 해당하는 속성의 전체 집합이 필요하므로, 실제로 나타나는 모든 레이블의 항목은 반드시 온전히 유지해야 합니다.
변형 휠이 나열되어 있으면, 도구는 가장 적합한 wheel 파일을 선택하기 위해 변형을 해석해야 합니다(SHOULD).
잠긴 패키지의 의존성 지정자에 나타나는 변형 Environment markers는 Evaluating variant markers에 설명된 대로, 해당 패키지 항목에 대해 선택된 휠의 맥락에서 평가됩니다. 잠금 파일에는 추가 맥락이 필요하지 않습니다. 마커는 해당 휠이 선택된 후에만 평가되며, 각 패키지 항목은 자신의 휠을 스스로 결정하기 때문입니다. 변형 마커는 의존성 지정자에서만 사용할 수 있으므로, packages.marker나 최상위 environments 키에는 나타날 수 없습니다. 이 둘은 모두 선택된 휠에 한정된 범위가 아니기 때문입니다.
제안하는 명세 변경 사항
pylock.toml Specification에 대해 제안하는 텍스트는 다음과 같습니다:
[packages.variants-json]
- 타입: 테이블
- 필수 여부: 아니요
- 함수:
- 패키지의 인라인 변형 선택 메타데이터입니다.
- 이 테이블의 구조는 JSON 스키마를 준수해야 합니다(MUST).
- 변형 인식 해석(variant-aware resolution)을 지원하는 도구는 반드시(MUST) 참조된 스키마에 대해 이 테이블을 검증해야 합니다.
- 변형 인식 해석을 지원하지 않는 도구는 이 테이블을 무시할 수 있지만(MAY), 잠금 파일을 다시 작성할 때는 이 테이블을 보존해야 합니다(SHOULD).
예시
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.variants-json]
"$schema" = "https://variants-schema.wheelnext.dev/peps/825/v0.1.1.json"
[packages.variants-json.default-priorities]
namespace = [ "x86_64", "aarch64", "blas_lapack" ]
[packages.variants-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"]} }
도구를 위한 권장 구현 로직 (비규범적)
색인에서 패키지 설치하기
색인에서 패키지의 특정 버전을 설치하라는 요청을 받았을 때, 제안하는 동작은 다음과 같습니다:
- 원격 색인에 해당 패키지를 질의합니다.
- 먼저 버전 제약 조건을 만족하는 패키지 버전을 선택합니다(이때 변형 메타데이터는 고려하지 않아도 됩니다).
- 사용 가능한 휠을 플랫폼 호환성 태그(Platform Compatibility Tags)에 따라 필터링합니다.
- 남은 휠 중에 변형 휠이 있는지 판단합니다. 없다면 비변형 휠과 동일하게 진행합니다.
- 휠 중 하나라도 Variant label을 가지고 있다면, 결합된 변형 메타데이터를 얻습니다. 일반적으로 이는 Index-level metadata 파일인
{name}-{version}-variants.json을 내려받는 것을 의미합니다. 소스가 그 파일을 제공하지 않는다면, 결합된 메타데이터를 대신 후보 휠에서 읽을 수 있습니다. 서로 다른 변형 레이블마다 휠 하나만 검사하면 되며, 그것도 휠 전체가 아니라 그 안의 Variant metadata 파일만 검사하면 됩니다. 이마저도 비용이 너무 크다면, 도구는 변형 휠을 호환되지 않는 것으로 취급하고 대신 비변형 휠로 진행할 수 있습니다. - 결합된 변형 메타데이터를 사용하여 변형 레이블을 변형 속성 집합으로 매핑합니다. 휠 파일 이름에 있는 레이블 중 일부가 결합된 메타데이터에 없다면, 해당 휠을 호환되지 않는 것으로 간주하거나, 5단계에서 설명한 대로 휠에서 직접 메타데이터를 읽습니다.
- 호환되는 변형 속성의 순서가 매겨진 목록을 얻습니다. 이를 위한 메커니즘은 후속 PEP에서 명시될 예정입니다.
- 호환되는 속성 목록을 바탕으로 Variant ordering에 따라 변형을 필터링하고 정렬한 뒤, 가장 선호되는 변형을 선택합니다. 일치하는 변형 휠이 없다면, 비변형 휠을 해당 규칙에 따라 사용합니다.
- 특정 버전에 대해 여러 휠이 같은 변형 레이블을 공유한다면, 플랫폼 호환성 태그와 빌드 번호에 따라 정렬하고 가장 적합한 휠을 선택합니다.
- 선택된 휠의 의존성을 읽고, Evaluating variant markers에 설명된 대로 선택된 휠의 레이블과, 그 휠의 변형 속성 중 대상 시스템이 지원하는 속성을 사용하여 의존성에 나타나는 Environment markers를 평가합니다.
4단계부터 8단계까지는 변형 휠을 위해 특별히 도입된 단계라는 점에 유의하십시오. 나머지 단계는 현재 설치 프로그램의 동작과 같습니다. 10단계는 새로운 환경 마커가 추가됨에 따라 수정됩니다.
같은 알고리즘은 색인이 아닌 다른 소스, 예를 들어 휠이 들어 있는 로컬 디렉터리에도 적용됩니다.
특정 로컬 휠 설치하기
로컬 wheel 파일을 설치하라는 요청을 받았을 때 제안하는 동작은 다음과 같습니다.
- 파일 이름에 변형 레이블이 없으면 비변형 휠과 같은 방식으로 진행합니다.
- 플랫폼 호환성 태그로 휠의 호환성을 검증합니다.
- wheel 파일 안의
*.dist-info/variant.json에서 Variant metadata를 읽습니다. - 호환되는 변형 속성의 순서가 매겨진 목록을 얻습니다. 이를 위한 메커니즘은 후속 PEP에서 명시할 예정입니다.
- 호환되는 속성으로 휠의 호환성을 검증합니다.
- 휠의 의존성을 읽고, Evaluating variant markers에 설명된 대로 휠의 레이블과 휠의 변형 속성 중 대상 시스템이 지원하는 속성의 레이블을 사용하여 의존성에 나오는 Environment markers를 평가합니다.
색인에 변형 휠 게시하기
변형 휠은 일반 휠과 똑같이 색인에 업로드됩니다. 각 패키지 버전마다 색인 수준의 {name}-{version}-variants.json 파일을 게시하는 방법에는 두 가지가 있습니다. 사용자가 파일을 준비하여 업로드할 수도 있고, 색인이 자동으로 생성할 수도 있습니다.
색인이 파일 생성을 담당하는 경우, 릴리스가 완전히 업로드될 때까지 파일 게시를 미루는 메커니즘을 사용해야 합니다(예: PEP 694).
{name}-{version}-variants.json 파일을 생성하려면, 주어진 패키지 버전에 대한 모든 변형 휠의 *.dist-info/variant.json 파일을 가져와 Default priorities 및 Variants 절에서 개별 키마다 명시한 대로 결합합니다. 결과는 휠을 처리하는 순서에 좌우되지 않습니다.
설치 예시(비규범적)
PyTorch가 다음과 같이 여러 변형 휠을 게시한다고 가정해 봅시다:
torch-2.13.0-{py}-{abi}-{platform}-cuda12.6.whl
^^^^^^^^
torch-2.13.0-{py}-{abi}-{platform}-cuda13.0.whl
^^^^^^^^
torch-2.13.0-{py}-{abi}-{platform}-cuda13.2.whl
^^^^^^^^
torch-2.13.0-{py}-{abi}-{platform}-rocm7.2.whl
^^^^^^^
torch-2.13.0-{py}-{abi}-{platform}-null.whl # CPU-only
^^^^
강조 표시된 파일 이름 부분이 변형 레이블입니다.
이 휠들은 각각 다음 내용을 담은 변형 메타데이터 파일을 포함합니다:
{
"$schema": "https://variants-schema.wheelnext.dev/peps/825/v0.1.1.json",
"default-priorities": {
"namespace": ["nvidia", "amd"]
},
"variants": {
// ...
}
}
모든 휠에서 variants 딕셔너리에는 변형 레이블인 키가 하나만 있으며, 그 값은 해당 레이블에 대응하는 모든 속성을 나열합니다. 예를 들어 cuda* 변형 휠에는 NVIDIA GPU와의 호환성을 나타내는 속성이 들어 있고, rocm* 변형 휠에는 AMD GPU와의 호환성을 나타내는 속성이 들어 있습니다. null 변형에는 속성이 없습니다.
이러한 변형 휠 외에도, 개별 휠의 변형 메타데이터를 병합한 torch-2.13.0-variants.json 파일이 게시됩니다. 이 파일은 variants 딕셔너리에 모든 변형 레이블과 그 속성이 포함된다는 점을 제외하면 예시와 내용이 같습니다.
패키지 관리자가 torch를 설치하라는 요청을 받으면 다음 순서로 진행합니다:
- 사용 가능한 휠을 파악하기 위해 색인을 조회합니다. 2.13.0이 최신 버전임을 확인하고 이를 선택합니다.
- 사용 가능한 2.13.0 휠을 플랫폼 호환성 태그로 필터링합니다. 현재 시스템과 호환되는 휠만 남습니다.
- 색인 응답에서
torch-2.13.0-variants.json파일을 찾아 내려받습니다. 그 내용을 읽어 변형 레이블에서 변형 속성 집합으로의 매핑과 네임스페이스 선호 순서를 파악합니다. variants딕셔너리에서 사용되는 모든 네임스페이스에 대해 호환되는 기능과 기능 값의 목록을 얻습니다. 이 목록에 없는 속성에 해당하는 변형 레이블을 가진 휠은 호환되지 않으므로 걸러집니다.예를 들면 다음과 같습니다.
cuda*레이블은 최소 CUDA 드라이버 버전을 값으로 지정하는nvidia :: cuda_version_lower_bound속성과, 지원되는 GPU 목록을 값으로 가지는nvidia :: sm_arch속성에 매핑됩니다. 설치 프로그램은 (드라이버가 있다면) 드라이버에 질의하여 호환되는 런타임과 호환되는 GPU 중 하나를 사용할 수 있는지 확인합니다. 드라이버, 호환되는 런타임 버전 또는 호환되는 GPU를 찾을 수 없으면 해당 휠은 목록에서 제거됩니다.- 마찬가지로
rocm*레이블은 지원되는 ROCm 버전을 지정하는amd :: rocm_version속성과, 지원되는 GPU 목록을 나타내는amd :: gfx_arch속성에 매핑됩니다. 설치 프로그램은 앞에서와 비슷한 방식으로 해당 드라이버에 질의합니다. 드라이버, 호환되는 런타임 버전 또는 호환되는 GPU를 찾을 수 없으면 해당 휠은 목록에서 제거됩니다. null레이블은 항상 빈 속성 집합에 대응하므로 항상 호환됩니다.
호환되는 NVIDIA GPU와 CUDA 런타임이 있는 시스템에서는
cuda*휠 중 적어도 하나와null휠이 목록에 남습니다.- 서로 다른 레이블을 가진 변형 휠이 여러 개 목록에 남아 있으면, Variant ordering의 알고리즘에 따라 결과를 정렬합니다. 가장 선호되는 레이블이 선택됩니다.
예를 들어 이 경우
cuda*휠은 4단계에서 얻은 목록을 사용하여 속성에 따라 정렬됩니다. 더 새로운 CUDA 버전이 선호되므로 CUDA 13용 휠이 CUDA 12용 휠보다 앞에 정렬됩니다.null휠은 항상 마지막에 정렬되므로, GPU 휠이 하나도 호환되지 않는 경우에만 선택됩니다. - 이 시점에서 같은 레이블을 가진 휠이 여러 개 목록에 남아 있으면, 최종 선택은 플랫폼 호환성 태그를 기준으로 이루어집니다. 이는 드문 경우이며 여기에는 해당하지 않습니다.
예를 들어 같은 휠 변형이
abi3태그와cp315태그로 모두 제공되는 경우에 이런 일이 생길 수 있습니다. Python 3.15용으로 설치할 때, 설치 프로그램은 태그를 기준으로 이 둘 중 하나를 선택합니다. - 선택된 휠의 메타데이터를 처리합니다. 해당 휠에 대응하는 변형 속성 가운데 4단계에서 시스템이 지원하는 것으로 확인된 속성만 추려서 Environment markers를 처리하는 데 사용합니다. 그 결과 CUDA 관련 의존성이 추가로 선택됩니다.
휠이 지원하는 아키텍처 중 하나만 지원하는 라이브러리에도 휠이 의존한다고 가정해 봅시다.
fast-gemm; "nvidia :: sm_arch :: 120_real" in variant_properties
오래된 GPU가 있는 시스템에서는
120_real이 4단계에서 확인된 지원 속성에 포함되지 않으므로, 속성을 추리는 과정에서 제거되어 이 의존성은 선택되지 않습니다. - 휠을 내려받아 설치합니다.
여러 소스에서 휠 설치하기 (비규범적)
이 글을 쓰는 시점에는 여러 소스에서 패키지를 설치하는 기능을 다루는 승인된 표준이 없으며, 기존 도구마다 구체적인 동작이 다릅니다. 이 문제는 정보성 문서인 PEP 766에 더 자세히 설명되어 있습니다. 변형 휠은 이러한 차이를 더욱 넓힙니다. 변형 메타데이터를 결합할 수 있게 해 주는 일관성 요건은 단일 소스 트리 안에서 성립하며, 독립적인 배포자들이 서로에 대해 이 요건을 충족해야 할 의무는 없습니다. 같은 변형 레이블이 서로 다른 속성에 대응될 수 있고, 독립적으로 정한 네임스페이스 순서가 서로의 확장일 필요도 없습니다. 두 소스의 메타데이터가 우연히 결합 가능한지 판별할 수는 있지만, 일반적인 경우에는 그 비용이 감당하기 어려울 만큼 크며, 결합할 수 없는 경우에는 올바른 답이 존재하지 않습니다. 이러한 이유로 이 명세는 동작을 표준화하려 하지 않습니다. 대신 이를 구현에 따라 정의되는 것으로 간주하고, 비변형 휠을 사용하는 방법을 포함하여 비규범적인 해결 방안 몇 가지를 제안합니다.
도구가 이 동작을 지원하지 않는 것도 전적으로 유효합니다. 여러 소스의 사용을 아예 지원하지 않거나, 둘 이상의 소스에 변형 휠이 포함된 경우 진행을 거부하는 방식이 이에 해당합니다.
서로 다른 소스의 변형 휠을 처리할 때는 각 소스의 변형 메타데이터를 따로 떼어 고려할 것을 권장합니다. 변형 레이블이나 네임스페이스가 일치한다고 해서 소스 간에 같은 의미를 가진다고 할 수는 없습니다.
우선순위에 따라 소스를 검색하는 도구(예: PEP 766의 “색인 우선순위”)는 Variant ordering 알고리즘을 사용하여 한 번에 한 소스의 변형을 정렬하고, 현재 소스에 사용 가능한 후보가 없을 때에만 다음 소스로 넘어갈 수 있습니다. 소스 간 메타데이터 병합은 필요하지 않습니다.
모든 소스의 후보를 모은 뒤 그중에서 선택하는 도구(예: PEP 766의 “버전 우선순위”)는 서로 다른 소스의 휠을 직접 비교해야 합니다. 어떤 패키지 버전에 대해 호환되는 변형 휠을 제공하는 소스가 정확히 하나뿐이라면, 일반적으로 변형 휠이 비변형 휠보다 선호되므로 해당 변형을 비변형 휠보다 먼저 선택할 수 있습니다. 여러 소스가 호환되는 변형을 제공하는 경우, 각 메타데이터 키에 해당하는 절에 명시된 대로 메타데이터가 일관될 때에만 그 메타데이터를 모호함 없이 결합할 수 있습니다.
메타데이터를 모호함 없이 결합할 수 없는 경우에는 유일하게 올바른 전역 순서가 존재하지 않습니다. 도구별로 선택할 수 있는 유효한 방안은 다음과 같습니다.
- 오류를 내며 설치를 거부하기
- 경고를 출력하고 변형 휠을 무시한 뒤, 비변형 휠 중에서 선택하는 방식으로 대체하기
- 호환되는 여러 변형 휠 중에서 선택할 때 결정적인 기준을 사용하기(예: 옵션 인자에서 먼저 지정된 색인의 휠을 우선하기)
- 사용자에게 추가 입력을 요청하기
이러한 방안은 전역적으로 최적인 선택을 보장하지는 않지만 유효한 설치 결과를 만들어 낼 수 있습니다. 결합된 순서를 조용히 지어내거나 충돌하는 레이블 대응 관계를 조용히 해소하는 것은 기술적으로 가능하지만 권장하지 않습니다. 어떤 소스도 선언하지 않은 의미를 지어내는 일이기 때문입니다.
근거
이 PEP는 원래 PEP 817로 제안되었던 더 큰 변형 휠 설계의 일부입니다. 그러나 설계가 복잡하기 때문에, 이를 차례로 앞의 것 위에 쌓아 올리는 더 작은 부분들로 나누기로 결정했습니다. 이 PEP는 그 시리즈의 첫 번째로, 파일 형식과 필요한 메타데이터, 색인 지원, 기본적인 도구 알고리즘을 포함한 기반을 제공합니다. 실제 변형 속성을 제공하거나 휠을 빌드하는 것과 같은 측면은 후속 PEP로 미룹니다.
변형 휠은 구조화된 Variant properties를 사용하여 다차원 휠 호환성 행렬을 표현합니다. 속성은 독립적으로 정의하고 관리할 수 있는 네임스페이스로 구성됩니다. 키-값 구조는 속성을 더 유연하게 만듭니다. 새로운 호환성 축을 추가하려면 새 키를 추가하기만 하면 됩니다. 이 구조는 AND 방식의 의존성(예를 들어 CPU 플러그인은 서로 다른 명령어 집합에 대응하는 여러 키를 정의할 수 있으며, 이 명령어 집합은 모두 패키지에서 사용되므로 모두 지원되어야 합니다)과 OR 방식의 의존성(예를 들어 GPU 플러그인은 여러 GPU 유형을 나열하는 단일 키를 정의하여 패키지가 그 유형을 모두 지원함을 나타낼 수 있으며, 따라서 사용자는 그중 하나만 갖추면 됩니다)을 모두 지원할 수 있습니다.
이 명세는 표현되는 속성의 수에 어떠한 공식적인 제한도 두지 않으며, 속성 집합이 매우 길어질 가능성(예를 들어 긴 GPU 목록이나 CPU 확장 집합)도 특별히 고려합니다. 정보가 지나치게 많아 wheel 파일 이름을 이해하기 어려워지거나 길이 때문에 기술적인 문제가 생길 수 있는 상황을 피하기 위해, 속성 목록은 휠 안에 저장되며, 패키지 유지 관리자가 정하고 사람이 읽을 수 있도록 의도된 짧은 레이블에 매핑됩니다.
변형 메타데이터는 Core metadata specifications에 추가되는 대신 별도의 JSON 형식 파일에 저장됩니다. 이 파일은 독립적으로 버전이 관리되며, 변형 메타데이터와 특별히 관련이 없는 도구는 그 호환성 규칙을 무시할 수 있습니다. 그 버전 관리 방식은 취지 면에서 코어 메타데이터의 버전 관리와 비슷합니다. JSON은 구조화된 데이터에 더 편리한 형식을 제공할 뿐만 아니라 TOML로부터 더 자연스럽게 변환할 수 있습니다(따라서 데이터를 pyproject.toml에서 가져올 수 있습니다).
휠 파일명만으로는 변형 휠을 선택하는 데 필요한 메타데이터를 충분히 얻을 수 없습니다. 도구가 여러 wheel 파일에서 변형 메타데이터를 직접 가져와야 하는 상황을 피하기 위해, 각 패키지 버전의 휠에서 얻은 메타데이터를 결합하여 다시 게시합니다. 이 메타데이터는 향후 버전에서 변형이 바뀔 수 있도록 단일 패키지 버전 범위로 한정됩니다.
색인 지원은 다음 세 가지 시나리오를 고려하는 것을 목표로 합니다:
- 파일 목록 응답의 일부로 추가 메타데이터를 포함할 수 없는 색인 구현입니다. 예를 들어, 웹 서버가 생성한 디렉터리 목록에서 직접 설치하는 경우가 여기에 해당합니다. 이 시나리오에 대응하기 위해, 색인 수준 메타데이터는 패키지 유지 관리자가 생성하여 휠과 나란히 둘 수 있는 일반 JSON 파일로 게시됩니다.
- 더 완전한 휠 지원을 갖추고 있지만 전체 변형 휠 지원을 당장 구현하기를 원하지 않는 색인 구현입니다. 이러한 색인은 사용자가 해당 JSON 파일을 업로드할 수 있도록 허용하고 휠 목록에 이를 반영해야 하지만, 변형 메타데이터를 직접 처리할 필요는 없습니다.
- 완전한 휠 변형 지원을 구현하는 색인 구현입니다. 이러한 색인은 업로드된 변형 휠을 파싱하고 색인 수준 메타데이터를 동적으로 생성합니다. 이 경우 JSON 파일 경로는 실제 파일이 아니라 API 엔드포인트로 취급됩니다.
JSON 형식에는 집합 타입이 없으므로, 메타데이터의 집합은 정렬된 리스트로 표현됩니다. 정렬은 재현성을 보장하며, 역직렬화 후 특정 필드를 다시 집합으로 변환할 필요 없이 딕셔너리 전체에 대해 동등 비교를 사용할 수 있게 해 줍니다.
변형 정렬 알고리즘은 변형 속성이 플랫폼 호환성 태그보다 우선한다는 가정하에 제안되었습니다. 변형 속성은 주로 사용자 선호를 표현하는 데 사용되기 때문입니다. 이는 플랫폼 태그가 달라질 수 있는 경우를 고려한 것입니다. 예를 들어 CUDA 변형이 다른 최소 libc 버전을 요구할 수 있는데, 이 경우 선택은 부수적인 플랫폼 태그 차이가 아니라 원하는 CUDA 선호에 따라 이루어져야 합니다.
변형 속성이 제공되는 방식은 향후 PEP에서 정의하겠지만, 호환되는 속성이 선호도에 따른 특정 순서로 제공된다는 것을 기본 가정으로 삼습니다. 이를 통해 범용 정렬 알고리즘을 사용할 수 있으며, 나중에 알고리즘을 변경하지 않고도 속성을 데이터로 정의할 수 있습니다.
기능과 값의 순서가 제공되는 방식은 향후 PEP에서 정의합니다. 그러나 네임스페이스는 독립적으로 관리되며 동등하게 취급되므로, 네임스페이스에 대한 표준 순서는 존재하지 않습니다. 그 대신 네임스페이스의 순서는 변형 메타데이터에 명시적으로 기술되며, 이 메타데이터는 다시 패키지 유지 관리자가 빌드 과정의 일부로 제공합니다.
실제 사용 사례의 대다수에서는 속성에 기반한 정렬로 충분합니다. 그러나 특수한 경우에는 서로 다른 두 변형 휠의 정렬 키가 같아질 수 있습니다. 이 경우에도 재현 가능한 결과를 제공하기 위해, 변형 레이블을 기준으로 한 대체 정렬을 수행합니다.
전환 기간을 원활하게 하기 위해 비변형 휠과 구별되는 널 변형(null variant)이라는 개념을 도입합니다. 이 PEP를 구현하는 도구는 이 변형을 항상 지원하며, 이 변형은 비변형 휠보다 우선합니다. 따라서 이를 이용하면 다른 어떤 변형도 지원되지 않는 경우와 변형 휠 자체가 전혀 지원되지 않는 경우에 대해 서로 구별되는 대체 수단을 제공할 수 있습니다. 예를 들어 PyTorch는 지원되는 GPU가 없을 때 사용되는 훨씬 작은 널 변형과, 기본 CUDA 버전용으로 빌드된 대체용 비변형 휠을 제공할 수 있습니다.
pylock.toml 통합은 파일이 그 자체로 완결되도록 변형 메타데이터를 인라인으로 포함합니다. 이렇게 하면 해당 파일을 가져오는 데 필요한 추가 네트워크 호출을 피할 수 있고, 특정 해시에 고정할 필요도 없어집니다. 변형 메타데이터가 갱신되거나 바이트 단위로 안정적인 출력을 보장하지 않는 방식으로 생성되어 색인의 파일이 바뀌면, 해시 고정은 문제를 일으킬 수 있습니다.
변형 환경 마커
변형 속성은 변형 휠을 설치할 때 서로 다른 두 시점에 관여하며, 그중 두 번째 시점만 마커와 관련됩니다. 설치 프로그램은 먼저 패키지 버전을 선택하고, 해당 버전의 휠을 플랫폼 호환성 태그로 거른 다음, 남은 변형 휠을 대상 시스템이 지원하는 속성과 대조하여 변형 속성에 따라 거르고 정렬합니다. 도구는 그 결과로 정해진 선택을 재정의할 수 있습니다. 그 이후에야 선택된 휠의 의존성을 읽고, 그 안에 나오는 변형 마커를 평가합니다.
따라서 휠을 거르고 정렬하는 기준은 마커가 아니라 변형 속성입니다. 마커는 휠 선택을 결코 좌우하지 않습니다. 마커는 개별 의존성 지정자의 적용 여부만 결정하며, 휠이 선택된 뒤에야 평가됩니다. 그렇지 않다면 마커가 참조하는 속성을 가진 휠이 무엇인지 알기도 전에 마커를 평가해야 합니다.
세 가지 집합 값 마커의 값은 대상 시스템이 지원하는 변형 속성만 남도록 걸러집니다. 휠은 자신이 실행되는 모든 값을 나열하므로, 이 단계가 없으면 그중 한 값을 조건으로 하는 의존성이 휠이 실행되는 모든 곳에 설치됩니다. 80_real부터 120_real까지의 GPU 아키텍처용으로 빌드한 휠이 120_real만 지원하는 라이브러리에 의존할 수 있습니다. 이는 단일 CPU 아키텍처만 지원하는 의존성을 platform_machine으로 조건 짓는 것과 비슷합니다. 어떤 기능의 모든 값에 대해 의존성이 존재한다면, 대신 그 의존성을 변형 패키지로 배포하고 조건 없이 의존하게 하여 선택을 Variant ordering에 맡길 수 있습니다.
이렇게 거르기 때문에 변형 마커는 환경 마커의 확립된 의미에서 벗어나지 않습니다. variant_properties의 모든 속성은 구조상 대상 시스템이 지원하는 속성입니다. 따라서 세 가지 집합 값 마커는 Dependency specifiers에 정의된 마커와 마찬가지로 환경을 기술합니다. 변형 휠에 고유한 것은 의미가 아니라 어휘입니다. 기존 마커는 고정된 환경 속성 집합을 모든 휠에 똑같이 노출합니다. 반면 변형 속성은 휠이 먼저 선언해야만 관찰할 수 있습니다. 즉 휠의 Variant metadata가 그 휠의 의존성 지정자가 환경의 어느 부분을 볼 수 있는지 결정합니다. variant_label은 예외입니다. 이 마커는 선택된 변형의 이름을 나타낼 뿐이며, 그 변형이 호환된다고 판단되었다는 사실 외에는 환경에 대해 아무것도 알려 주지 않습니다.
이 설계에서 주목할 만한 결과는 다음 세 가지입니다:
- 거르기는 기능이나 네임스페이스 전체를 제거하지 않습니다. 휠은 자신이 선언한 모든 기능에 대해 대상 시스템이 적어도 하나의 값을 지원할 때만 선택될 수 있기 때문입니다(Variant properties 참조). 따라서
variant_features와variant_namespaces는 휠이 빌드 대상으로 삼은 모든 기능과 네임스페이스를 항상 나열합니다. 이 때문에 Variant ordering에서 허용하는 재정의는 호환성 필터를 넘어설 수 없습니다. 지원되지 않는데도 선택된 휠은 속성이 걸러져 사라질 수 있고, 그러면 그 속성을 조건으로 하는 의존성도 아무 경고 없이 사라집니다. - 널 변형은 속성이 하나도 없으므로, 널 변형에서
variant_label은"null"이고 세 가지 집합 값 마커는 빈 집합입니다. 따라서variant_label은 널 변형과 비변형 휠을 구별하는 유일한 마커입니다. - 거르기는 속성을 제거하기만 하므로, 휠이 선언하지 않은 속성, 기능 또는 네임스페이스를 참조하는 마커는 어떤 환경에서도 충족될 수 없습니다. 따라서 이러한 마커는 빌드 시점에 결정할 수 있으며, 이 덕분에 Backwards Compatibility에서 설명하는 부분 평가가 가능합니다.
하위 호환성
변형 휠은 휠 파일 이름에 Variant label 구성 요소를 하나 더 추가합니다. 파일 이름을 완전하게 검증하는 단계에서는 이러한 휠을 거부해야 합니다:
- 빌드 태그와 변형 레이블이 모두 있으면 파일 이름의 구성 요소가 너무 많습니다. 예시:
numpy-2.3.2-1-cp313-cp313t-musllinux_1_2_x86_64-x86_64_v3.whl ^^^^^^^^^^ - 변형 레이블만 있으면 세 번째 위치의 Python 태그가 빌드 번호로 잘못 해석됩니다. 빌드 번호는 숫자로 시작해야 하므로, 해당 파일 이름은 유효하지 않은 것으로 간주됩니다. 예시:
numpy-2.3.2-cp313-cp313t-musllinux_1_2_x86_64-x86_64_v3.whl ^^^^^
현재 숫자로 시작하는 Python 태그는 없습니다. 모호성이 없도록 보장하기 위해, 이 명세는 앞으로 이를 강제합니다. 이 글을 작성하는 시점에 휠 설치에 흔히 사용되던 도구들은 그러한 검증 알고리즘을 구현하고 있었으므로, 변형 휠이 실수로 설치될 위험 없이 변형 휠을 비변형 휠과 함께 색인에 게시할 수 있습니다.
파일 이름을 완전히 검증하지 않는 도구는 변형 휠의 일부 또는 전부를 일반 휠로 사용하게 됩니다. 해당 도구가 변형 휠을 특별히 고려해야 하는 경우, 이로 인해 예기치 않은 동작이나 오작동이 발생할 수 있습니다.
wheel 파일을 처리하는 라이브러리와 그 소비자는 새로운 파일 이름 구성 요소와, 경우에 따라서는 새로운 메타데이터를 처리할 수 있도록 업데이트되어야 합니다. 예를 들어, packaging 프로젝트에서 parse_wheel_filename() 함수를 어떻게 조정할지에 대한 열린 논의가 있습니다.
변형 레이블이 추가되면 파일 이름의 길이가 늘어납니다. Windows처럼 전체 경로 길이 제한이 낮은 플랫폼에서는 긴 파일 이름이 문제가 됩니다. 그러나 이름과 버전 구성 요소에 이미 제한이 없다는 점을 고려하여, 이 PEP에서는 특정한 제한을 두지 않습니다. PyPI 등 다른 주체가 전체 파일 이름 길이에 제한을 둘 수 있습니다.
이 명시적인 비호환성을 제외하면, 이 명세는 바이너리 패키지 형식에 최소한의 비침습적인 변경만을 가합니다. Variant metadata는 .dist-info 디렉터리 안의 별도 파일에 저장됩니다. 변형과 직접 관련이 없는 도구는 파일 이름 검증 알고리즘을 (있는 경우) 갱신하고 해당 디렉터리의 내용을 보존하기만 하면 됩니다.
새로운 Environment markers가 휠 의존성에 사용되면, 해당 휠은 기존 도구와 호환되지 않습니다. 예를 들어, 색인에서 가져온 의존성에서 이러한 마커를 만나면 pip는 백트래킹하여 (가능한 경우) 더 오래된 버전의 의존성을 사용합니다. 이는 환경 마커 설계의 일반적인 문제이며, 휠 변형에만 해당하는 문제가 아닙니다. 빌드 시점에 환경 마커를 부분적으로 평가하고, 비변형 휠에서 변형 휠에만 해당하는 마커나 의존성을 제거하는 방식으로 이 문제를 우회할 수 있습니다.
보안 영향
변형 휠이 존재하면 일부 변형이 다른 변형보다 덜 면밀하게 검토될 수 있으며, 그 결과 더 쉬운 공격 대상이 될 수 있습니다. 특히 변형 휠 지원이 보편화되면, 일부 패키지의 비변형 휠은 오래된 도구를 사용하는 사용자만 사용하게 될 수도 있습니다. 그러나 이러한 공격은 패키지 게시 워크플로가 이미 침해되었음을 전제로 하며, 그런 경우에는 컴파일된 확장 모듈을 수정하는 등 더 현실적인 공격 경로가 존재합니다.
교육 방법
이 PEP는 도구 작성자를 대상으로 합니다. 이 PEP의 변경 사항은 Binary distribution format 및 기타 PyPA 명세에 통합될 것입니다. 최종 사용자에게 변형을 가르치는 방법은 사용자 경험의 세부 사항을 다루는 후속 PEP에서 다룰 것입니다.
참조 구현
variantlib 프로젝트에는 완전한 변형 휠 솔루션의 참조 구현이 포함되어 있습니다. 이 구현은 이 PEP를 준수할 뿐만 아니라 그 범위를 넘어, 보류된 항목 중 일부에 대한 예시 솔루션도 제공합니다.
변형 휠을 설치하는 클라이언트는 uv 브랜치에 구현되어 있습니다.
기각된 아이디어
예측 가능한 변형 레이블
이 명세에서는 변형 레이블을 임의로 정할 수 있게 하고, 변형 속성을 레이블에 직접 표현하는 대신 Variant metadata 파일을 통해 레이블에 매핑하도록 제안합니다. 변형 속성으로 변형 레이블을 만드는 것도 기술적으로는 가능할 수 있습니다. 하지만 그러려면 일부 플랫폼에서 문제를 일으킬 만큼 매우 긴 파일 이름을 허용하거나 변형 속성의 개수를 임의로 제한해야 하므로, 이 명세가 다차원 호환성 행렬을 다루기에 덜 적합해집니다.
대안으로 변형 속성의 해시를 사용하는 방식이 있었습니다. 이 방식은 기술적으로 타당하며 변형 속성 집합이 아무리 커도 짧고 고유한 레이블을 제공할 수 있지만, 레이블이 불투명해지므로 읽거나 의미를 추론하기 어렵습니다.
플랫폼 호환성 태그의 일부로서의 변형 레이블
이 명세는 변형 레이블을 별도의 구성 요소로 추가하므로 기존 도구와의 호환성이 깨집니다. 대신 Platform 호환성 태그 중 하나에 이를 덧붙여 부분적인 호환성을 유지하는 것도 기술적으로는 가능할 수 있습니다. 이 경우 설치 프로그램은 플랫폼(또는 Python 인터프리터) 비호환성을 이유로 휠을 거부하지만, 다른 도구는 여전히 이를 사용할 수 있습니다. 그러나 저자들은 하위 호환성을 깨는 편이 더 안전하다고 판단했습니다. 또한 태그를 재사용하면 휠 레이블이 압축된 태그 집합과 잘못 결합될 잠재적 위험이 있었습니다. 예를 들어 manylinux_2_27_x86_64.manylinux_2_28_x86_64+x86_64_v3 태그는 manylinux_2_27_x86_64 부분 때문에 호환되는 것으로 잘못 판정됩니다.
플랫폼 호환성 태그의 완전한 대체
기술적으로는 현재 플랫폼 호환성 태그로 전달하는 정보를 변형 속성으로 전달하고, 파일 이름에서 이러한 명시적 태그를 제거하는 것도 충분히 가능합니다. 그러나 이 방식을 택하지 않고, 추가 변형이 필요 없는 휠은 기존 파일 이름을 유지하기로 했습니다. 기존 워크플로를 모두 수정하는 데 드는 노력이 더 간결하고 일관성이 약간 더 높은 명명 방식의 이점을 정당화한다고 보지 않기 때문입니다.
wheel 파일에서 순서 정보 제거
이 명세에서는 휠 변형의 순서를 정하는 데 필요한 모든 데이터를 변형 메타데이터(Default priorities 딕셔너리)에 저장하도록 제안합니다. 이 데이터는 프로젝트별 공통 출처에서 비롯되어(후속 PEP에서 pyproject.toml 파일과의 통합을 제안할 예정입니다) 빌드 시 모든 변형 휠에 복사되고, 그 뒤 Index-level metadata 파일에도 복사될 것으로 예상됩니다.
이렇게 하면 순서 데이터가 휠 수준의 속성이 되어, 특정 휠에 한번 삽입되면 다른 휠에 관한 내용을 말하게 된다는 주장이 있었습니다. 실제로는 그렇지 않습니다. 이 데이터는 휠에 복사되는 프로젝트 수준의 메타데이터이며, 휠에 담기는 다른 프로젝트 메타데이터와 마찬가지로 다른 휠을 참조하지 않습니다. 순서 데이터에는 특정 릴리스에 변형 휠이 하나도 없는 네임스페이스도 지정할 수 있으며, 이 형식의 어떤 부분도 특정한 다른 휠의 존재에 의존하지 않습니다.
또한 이 설계는 색인 수준 메타데이터 파일이 1차 데이터 출처가 아니라 캐시가 되도록 특별히 보장합니다. 모든 데이터가 휠에 저장되므로 다음이 가능합니다.
- 추가 입력 데이터 없이 사용 가능한 휠만으로 색인 수준 메타데이터 파일을 생성할 수 있습니다. 특히 이를 통해 색인은 휠 업로드에 사용하는 도구나 워크플로를 전혀 바꾸지 않고도 이 파일을 자동으로 생성할 수 있습니다.
- 로컬 디렉터리에서 설치하는 경우처럼 색인 수준 메타데이터 파일이 없을 때에도 여러 휠 가운데에서 선택할 수 있습니다.
서로 다른 시점에 서로 다른 도구로 빌드한 두 휠의 메타데이터가 일치하지 않게 될 실제 위험은 분명히 있습니다. 그러나 이 명세는 Metadata consistency에 설명된 대로 일관성을 요구하며, 그 책임을 해당 패키지 버전의 게시자에게 부여합니다.
또한 순서 데이터를 변형 메타데이터에서 분리하면 도구가 표준 형식으로 된 해당 데이터의 재정의를 받아들일 수 있다는 의견도 있었습니다. 그러나 이 두 가지는 서로 관련이 없습니다. 그러한 형식은 어느 방식으로든 도입할 수 있으며, 예를 들어 변형 메타데이터의 일부를 사용할 수 있습니다.
범위 외
다음 문제는 이 시리즈의 후속 PEP로 미룹니다:
- 변형 네임스페이스의 거버넌스
- 어떤 변형 속성이 시스템과 호환되는지 판별하기
- 정적 데이터를 사용하여 호환성 감지를 재정의하기
- 변형 휠 빌드하기
미해결 문제
이 PEP가 승인되기 전에 이 질문들이 해결되어야 합니다.
변형 환경 마커의 사용
변형 Environment markers의 설계는 아직 확정되지 않았습니다. 동적 의존성을 통해서도 같은 효과를 얻을 수 있습니다. 미해결 질문은 한 릴리스 전체에 걸쳐 의존성 메타데이터를 정적으로 유지하면서 그 효과를 얻는 데 마커가 올바른 메커니즘인지 여부입니다. 이 문제는 이 스레드에서 논의되고 있습니다. 이 명세는 현재 설계를 반영합니다.
감사의 글
이 작업은 Python 패키징 커뮤니티의 많은 분들께서 기여와 피드백을 주시지 않았다면 불가능했을 것입니다. 특히 이 PEP를 다듬는 데 도움을 주신 다음 분들께 감사드립니다(알파벳순):
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, Zanie Blue.
변경 이력
- 2026년 8월 19일
- 색인이 관련 없는 선택적 속성을 게시하지 않고 도구가 이를 무시하도록 요구하여 색인 규칙을 강화했습니다.
- 색인 수준 메타데이터 파일에서 누락된 변형을 처리하는 방법에 대한 제안을 추가했습니다.
- 색인 수준 메타데이터 파일을 비표준 휠 출처에서도 사용할 수 있음을 명시했습니다.
- 2026년 8월 10일
- 명세의 대부분을 Index-level metadata에서 분리하고, 이것이 휠이 색인에 게시되는 시나리오를 위한 최적화일 뿐임을 명확히 했습니다.
- 여러 출처에서 변형 휠을 설치하는 방법에 대한 비규범적 지침을 추가했습니다.
- 명시적인 “구현 요구 사항” 섹션을 추가했습니다.
- 개별 변형 메타데이터 키의 범위를 명확히 하고, 각 키에 대한 일관성 요구 사항을 함께 명시했습니다.
- 거부된 아이디어에 “wheel 파일에서 순서 정보 제거”를 추가했습니다.
default-priorities.feature와default-priorities.property를 제거했습니다.- 스키마 버전 관리에 의미 기반 버전 관리(semantic versioning)를 사용하도록 하고, 그에 따른 하위 호환성 영향을 반영했습니다.
- 환경 마커 내용을 개선했습니다.
variant_properties마커가 메타데이터에 지정된 모든 속성이 아니라 시스템과 호환되는 변형 속성을 사용하도록 했습니다. - 환경 마커 사용법을 설명하도록 pylock.toml 절을 수정했습니다.
- 언어와 설계의 일관성을 위해 여러 작은 부분을 수정했습니다.
- 2026년 5월 11일
- 플랫폼 호환성 태그를 완전히 대체하는 방안을 기각된 아이디어에 추가했습니다.
- 정렬 알고리즘과 색인 지원에 대한 해석을 명확히 했습니다.
- 2026년 4월 6일
- Python 태그가 숫자로 시작해서는 안 된다는 공식 요구 사항을 추가했습니다.
- 파일 이름 전체를 검증하지 않는 도구와 관련된 하위 호환성 우려 사항을 보강했습니다.
- 2026년 3월 9일
variants딕셔너리의 기능 값은 집합이며, 직렬화할 때 정렬해야 한다는 점을 명확히 했습니다.- 변형 메타데이터 병합 규칙을 변경하여 휠 순서와 관계없이 결과가 같아야 한다고 명시했습니다. 이렇게 하면 모호한 결과를 피하려는 목표가 더 명확하게 전달됩니다.
- 2026년 2월 17일
- PEP 817 초안에서 분리된 최초 버전입니다.
- 변형 정렬 알고리즘을 수정하여, 각 기능에 대해 호환되는 모든 값이 아니라 시스템과 호환되는 가장 좋은 값을 기준으로 변형을 정렬하도록 했으며, 변형 레이블을 기준으로 하는 대체 정렬을 추가했습니다.
- 변형 레이블의 길이 제한을 없앴습니다.
pylock.toml통합 방식을 변경하여, URL과 해시를 저장하는 대신 변형 메타데이터를 인라인으로 포함하도록 했습니다.
부록
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.