PEP 803 – “abi3t”: 자유 스레드 빌드를 위한 안정 ABI
- Author:
- Petr Viktorin <encukou at gmail.com>, Nathan Goldbaum <nathan.goldbaum at gmail.com>
- Discussions-To:
- Discourse thread
- Status:
- Final
- Type:
- Standards Track
- Requires:
- 703, 793, 697
- Created:
- 19-Aug-2025
- Python-Version:
- 3.15
- Post-History:
- 08-Sep-2025, 20-Nov-2025, 16-Feb-2026
- Resolution:
- 30-Mar-2026
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
“자유 스레드 Python을 위한 안정 ABI”(줄여서 abi3t)라고 하는 안정 ABI의 새로운 변형을 추가합니다.
abi3t는 기존 안정 ABI(abi3)를 기반으로 하지만, PyObject 구조체를 불투명하게 만듭니다. 이에 따라 사용자는 모듈과 대부분의 클래스를 정의하는 것과 같은 일반적인 작업을 위해 새로운 API로 마이그레이션해야 합니다.
실제로 abi3t 3.15는 abi3 3.15와 호환됩니다. 확장 모듈 작성자는 두 ABI를 동시에 명시적으로 컴파일하고, 휠 태그 abi3.abi3t를 사용하여 호환성을 표시하는 것이 좋습니다.
용어
이 PEP에서는 “GIL 활성화 빌드”를 “자유 스레드 빌드”의 반의어로 사용하며, 이는 Py_GIL_DISABLED 없이 빌드된 인터프리터 또는 확장을 의미합니다.
동기
현재 안정 ABI는 자유 스레드 빌드에서 사용할 수 없습니다. 자유 스레드 Python에서 안정 ABI용 확장 모듈을 빌드하려고 하면 실패합니다. 즉, Py_LIMITED_API와 Py_GIL_DISABLED 전처리기 매크로가 모두 정의된 경우입니다. CPython의 GIL 활성화 빌드용으로 빌드된 확장 모듈은 자유 스레드 빌드에서 로드되지 않거나 충돌합니다.
Steering Council은 PEP 779에 대한 승인 게시물에서 “자유 스레딩을 위한 안정 ABI가 Python 3.15에 맞춰 준비되고 정의될 것으로 예상합니다”라고 밝혔습니다.
이 PEP는 자유 스레딩을 위한 안정 ABI를 제안합니다.
배경 및 요약
Python의 Stable ABI(abi3로 줄여 부름)는 PEP 384 및 PEP 652에 정의되어 있으며, 여러 CPython 인터프리터 마이너 버전에서 로드할 수 있는 확장 모듈을 컴파일하는 방법을 제공합니다. 여러 프로젝트에서는 이를 사용하여 각 릴리스마다 빌드하고 배포해야 하는 휠(바이너리 산출물)의 수를 제한하고, 또는 Python의 사전 릴리스 버전으로 더 쉽게 테스트합니다.
자유 스레드 빌드(PEP 703)가 궁극적으로 기본값(PEP 779)이 될 전망인 만큼, 이러한 빌드에서 안정 ABI를 사용할 수 있도록 하는 방법이 필요합니다.
현재 안정 ABI는 버전이 지정되며, 안정 ABI 3.X용으로 빌드된 확장 모듈은 CPython 3.X 및 그 이후의 모든 버전과 ABI 호환됩니다. 다만 실제로는 CPython의 버그로 인해 호환되지 않는 경우가 있습니다.
그러나 이러한 순방향 호환성은 CPython이 노출하는 API의 일부(함수, 구조체 등)에 대해서만 보장됩니다. 안정 ABI를 대상으로 하는 확장 모듈은 제한된 API라고 하는 이 하위 집합만 사용해야 합니다. “옵트인” 전처리기 매크로 Py_LIMITED_API가 정의되면 CPython 헤더는 제한된 API만 노출합니다.
이 PEP는 자유 스레드 빌드를 위한 안정 ABI(줄여서 abi3t)를 제안합니다. 여기에는 CPython 3.15 이상에서 GIL 활성화 빌드와 자유 스레드 빌드 모두와 호환되는 확장 모듈을 컴파일하는 데 필요한 추가 API 제한, 이러한 제한을 옵트인하기 위한 대응 매크로, 그리고 확장 모듈이 그러한 호환성을 표시하는 데 사용해야 하는 명명 및 태그 지정 방식이 포함됩니다.
생태계 유지 관리자는 유지 관리 부담이 줄어들기를 원합니다.
제한된 API 및 안정 ABI 휠의 주요 장점은 새로운 Python 버전을 릴리스 당일부터 지원할 수 있다는 점입니다. 안정 ABI 휠이 없다면 유지 관리자는 CPython 릴리스를 면밀히 따라가 CPython 베타 기간에 휠을 제작하거나, 새로운 CPython 버전 지원을 요청하는 불가피한 사용자 요청을 처리해야 하는 선택에 놓입니다.
Cryptography
cryptography 프로젝트는 가장 최근 릴리스에서 48개의 휠 파일을 배포했습니다. Cryptography는 최신 제한된 API 버전에서 사용할 수 있는 최적화를 활성화하기 위해 cp38 및 cp311 안정 ABI 각각에 대해 14개의 휠을 배포한다는 점에서 다소 특이합니다. 또한 cp314t 휠 14개와 PyPy용 휠 6개를 추가로 배포합니다. 자유 스레드를 위한 안정 ABI가 없다면 Python 3.15에서 cryptography는 자유 스레드 빌드의 두 버전을 지원하는 데 PyPI에서 GIL 활성화 빌드의 EOL이 아닌 모든 버전을 지원하는 것과 거의 같은 양의 공간을 사용하게 됩니다.
Cryptography 유지 관리자 Alex Gaynor는 Discourse에서 자유 스레드 안정 ABI에 대한 열망을 expressed a desire라고 표현했습니다:
PyCA 유지 관리자들의 관점에서 명확히 말씀드리면, O(1) 빌드를 유지하는 한 괜찮습니다. 우리가 할 수 없고 하지 않을 일은 모든 Python 릴리스마다 새로운 빌드가 필요한 O(n) 방식입니다.
PEP 작성자 중 한 명이 Libera IRC의 #pyca 채널에서 Alex에게 현재 의견을 물었을 때, 그는 다음과 같이 말했습니다:
제가 언급하고 싶은 또 한 가지는abi3에서 정말 가치 있는 점이, 기존 휠이 새 Python 버전에서도 계속 작동한다는 것입니다. Python 릴리스별 휠을 제공한다면 Python 릴리스 주기의 여러 시점에 많은 작업을 해야 합니다(당시에 별도의 릴리스를 계획하고 있지 않다면 새 휠을 추가하기 위한 백포트 릴리스까지 포함합니다).유지 관리자로서 우리는 그런 식으로 “시간에 쫓기는” 상황을 피하도록 작업을 구성하는 것을 정말 선호합니다.
moocore
moocore project ships 일곱 개의 abi3 휠을 제공합니다. 자유 스레드 빌드 지원 추가 문제가 moocore 이슈 트래커에서 came up on the moocore issue tracker 때, 유지 관리자 Manuel López-Ibáñez는 이슈를 보고한 사람에게 다음과 같이 알렸습니다:
정말 필요하지 않다면 3.14 자유 스레드용으로 빌드하고 싶지 않습니다.
이후 자유 스레드 빌드에서 제한된 API를 지원하기 위한 추적 이슈를 발견한 그는 다음과 같이 논평했습니다:
참고로 python/cpython#111506은 자유 스레드 Python을 지원하도록 안정 ABI를 확장하는 것에 관한 문제입니다. 그렇게 된다면 moocore의 빌드는 각 Python 버전마다 새 휠을 빌드할 필요 없이 일반 Python 버전과 자유 스레드 Python 버전 모두에서 작동할 것입니다.[…]
Python 3.15가 릴리스되면 이 문제를 다시 검토하겠습니다. ABI가 안정적이기를 바랍니다(더 나아가 자유 스레딩이 기본값이 되기를 바랍니다).
Pydantic
Pydantic 유지 관리자 David Hewitt는 observed라고 밝혔습니다:
Pydantic은 Rust와 PyO3를 사용해 빌드한 네이티브 코어용 휠을 배포합니다. 최신 릴리스인 pydantic-core distributed 는 112개의 휠을 배포했으며, Android, iOS, wasm 등 더 많은 환경이 추가됨에 따라 이 수는 늘어날 예정입니다. Pydantic은 기능 집합이 지나치게 미성숙했기 때문에 역사적으로 안정 ABI를 사용해 배포하지 않았습니다. Pydantic의 많은 기능은 핫 루프에서 C API를 통해 Python 객체와 상호 작용하므로 성능이 핵심적인 관심사입니다. 안정 ABI가 성숙함에 따라 Pydantic이 tier 2 플랫폼을 안정 ABI로 전환하고(향후에는 tier 1 플랫폼도 전환할 수 있습니다), 이를 통해 빌드하고 테스트하고 배포해야 하는 휠의 수를 크게 줄이는 것이 이상적입니다.자유 스레딩이 안정 ABI를 채택하지 않으면, 자유 스레딩이 기본값이 되고 유일한 빌드 방식이 될 때 위의 모든 이점이 사라진다는 점을 강조하고 싶습니다.
SciPy
SciPy 프로젝트는 네 가지 CPython 버전을 지원하기 위해 last release에 대해 버전별 휠 파일 60개를 업로드했습니다. PyPy용 휠은 업로드하지 않습니다.
이 제안에 대한 질문을 받았을 때 SciPy 운영 위원회 의장 Ralf Gommers는 said:
SciPy와 과학 Python 생태계의 여러 프로젝트는 안정 ABI를 사용하기 시작하는 데 상당한 관심이 있으며, 특히 더 많은 휠을 제공하는 providing more wheels 데 따른 유지 관리 부담을 줄이는 데 관심이 있습니다. 최근 CPython, Cython 및 NumPy 릴리스와 함께 이제 이것이 가능해 보입니다. 성능 비용은 수용할 수 있을 만큼 작아 보이지만, 실제로 전환한 뒤에야 그 평가에 대한 확신을 얻을 수 있을 것입니다.
Python 3.15에서 새로운 자유 스레드 안정 ABI를 제공하면, SciPy 프로젝트가 안정 ABI 휠로의 전환을 고려할 때 자유 스레드 빌드에 안정 ABI가 없다는 점을 고려할 필요가 없게 됩니다.
바인딩 생성기
moocore와 cryptography는 모두 C API와 인터페이스하기 위해 바인딩 생성기를 사용합니다. cryptography는 PyO3와 CFFI를 사용하는 반면, moocore는 CFFI만 사용합니다. CFFI와 PyO3는 모두 다양한 빌드 구성을 지원하도록 C API를 추상화하는 모든 세부 사항을 이미 처리하므로, 한쪽 빌드에서만 사용할 수 있는 API를 사용하도록 확장 타입을 수고롭게 이식할 필요가 없습니다.
바인딩 생성기를 사용하면 이러한 프로젝트에서 새로운 안정 ABI를 신속하게 채택할 수 있습니다. CPython의 main 브랜치에 정의된 실험적 _Py_OPAQUE_PYOBJECT 플래그를 사용한 초기 테스트에 따르면, abi3.abi3t 태그를 고려하도록 패치된 패키징 도구를 사용하면 PyO3, CFFI, Cython이 모두 PEP 803과 함께 작동합니다.
PyO3 유지 관리자인 David Hewitt said는 이 제안을 지지하며 다음과 같이 말했습니다:
PyO3는 안정 ABI를 사용함으로써 큰 이점을 얻습니다. 이 프레임워크의 가장 큰 과제 중 하나는 광범위한 Python / OS / CPU / 환경 조합을 추상화해야 한다는 점입니다. 또한 이러한 각 환경에서 안정 ABI로 빌드할 수 있는 가능성도 제공합니다(지정된 최소 Python 버전의 안정 ABI를 대상으로 합니다). 목표는 언제나 PyO3가 제공하는 모든 기능이 이러한 모든 조합에서 동일하게 작동하도록 하는 것입니다(특정 기능에 접근하기 위해 Python 버전에 하한을 두는 경우도 있습니다). 현재 Python 3.7 이상을 지원합니다. 안정 ABI에 추가되는 모든 기능은 PyO3가 특정 기능을 지원하기 위해 추가적인 조건부 코드를 도입할 필요가 없게 하는 매우 반가운 약속입니다. 장기적으로는 이전 Python 버전에 대한 지원이 중단됨에 따라 PyO3가 코드 경로를 단순화할 수 있어 유지 관리 부담을 통제하는 데 도움이 됩니다.
이 제안에 대한 의견을 요청받았을 때 Cython 유지 관리자인 David Woods said는 다음과 같이 말했습니다:
Cython은 배포하는 휠의 수 때문에 큰 문제를 겪지는 않습니다. 궁극적으로 순수 Python으로도 잘 작동하기 때문입니다. 일부 소규모 플랫폼에는 Stable ABI 휠로 휠을 배포하기도 하지만, 실제로 필요해서라기보다는 “dogfooding”에 가깝습니다. 따라서 제가 유용하게 사용할 것이기 때문이 아니라, 다른 사람들이 유용하게 사용할 것에 대비하여 이를 추가하는 것입니다.다만 이것의 성능 절충이 많은 Cython 사용자에게는 지나치게 큰 것으로 드러날 수 있다는 점이 약간 우려됩니다(다른 바인딩 도구에서는 절충점이 다를 가능성도 있습니다). 일반적인 컴파일 모드를 없애는 것은 아니므로 큰 재앙은 아닙니다. 따라서 사람들은 각자 자신에게 맞는 절충점을 자유롭게 선택할 수 있습니다.
이 PEP가 일부 질문과 작업을 미해결 상태로 남겨 둔다는 점에 유의해야 합니다. Wenzel Jakob – C++ 바인딩 제너레이터인 nanobind의 유지 관리자 – 는 noted한 바와 같이, 추가 API가 필요하지만 이 PEP의 범위에서는 제외되어 있습니다:
[PyVarObject에서 파생된 객체의] N번째 데이터 항목에 대한 포인터를 어떻게 얻을 수 있는지 저에게는 명확하지 않습니다.이를 해결할 수 있다면 nanobind는 이 새로운
abi3t컴파일 대상을 채택할 수 있을 것입니다.
근거
abi3t의 설계에는 여러 선택 사항/가정/제약 조건이 포함됩니다.
별도의 ABI
새로운 ABI(abi3t)는 기존 안정 ABI(abi3)와 개념적으로 별개로 취급됩니다. 실제로는 abi3t와 호환되는 모든 확장이 abi3와도 호환되더라도 그렇습니다.
(더 정확히 표현하면 abi3t의 허용된 API 집합은 abi3의 subset이며, abi3t의 호환 인터프리터 집합은 abi3의 superset입니다. 이 때문에 머리가 어지러운데, 이것이 이들을 별도로 유지하는 이유 중 하나입니다.)
확장은 abi3t와 abi3 모두를 대상으로 명시적으로 컴파일해야 하며, 패키징 휠 태그(abi3.abi3t)와 런타임 ABI 검사(Py_mod_abi)를 통해 두 ABI를 동시에 지원한다는 사실을 명시적으로 나타내야 합니다.
이러한 명시성은 abi3t 지원이 abi3 지원을 암시하도록 하는 것보다 몇 가지 장점이 있습니다.
- 태그를 통해 확장이 GIL 활성화 빌드와 호환되는지, 그리고
abi3의 기존 하위 호환성 보장이 적용되는지를 명확히 알 수 있습니다. - 구현 측면에서 특정 자유 스레드 인터프리터가 지원하는 태그 집합(packaging 함수
packaging.tags.sys_tags가 반환하는 집합)은 이에 대응하는 GIL 활성화 빌드의 집합과 크기가 같습니다. - 이를 통해 향후 ABI가 약간 달라질 수 있으며, 둘 다 동시에를 권장 컴파일 대상으로 유지하면서 특수한 경우에는
abi3t전용 확장도 허용할 수 있습니다.
ABI를 개념적으로 분리하여 유지하는 데 대한 실질적인 예외 하나는 파일 이름 태그 섹션에서 설명합니다.
대안에 대한 자세한 내용은 다음 Rejected Ideas 섹션을 참조하십시오:
현재는 하위 호환성이 없습니다.
CPython 헤더는 CPython 3.14 이하에서 abi3t로 컴파일하는 것을 허용하지 않습니다. 이를 필요로 하는 프로젝트는 3.14 자유 스레드 인터프리터용 확장과 이전 abi3용 확장을 별도로 빌드할 수 있습니다.
그러나 CPython 3.14 이상의 자유 스레드 빌드와 GIL 활성화 빌드 모두와 호환되는 확장을 빌드하는 것은 기술적으로 가능합니다. 이 영역에서 실험을 활성화하려면 패키지 설치 도구가 이러한 확장을 지원할 수 있도록 준비할 것을 권장합니다. 자세한 내용은 rejected idea를 참조하십시오.
확장에는 소스 변경이 필요합니다.
abi3t를 사용하려면 확장 작성자가 코드에 상당한 변경을 가해야 합니다.
아직 이렇게 할 수 없는 프로젝트는 abi3를 계속 사용하면서 자유 스레드 빌드의 특정 버전용으로 동일한 소스를 컴파일할 수 있습니다. (특정 버전용으로 컴파일할 때는 abi3t에서 제거된 API도 계속 사용할 수 있으며, 3.15t도 이에 포함된다는 점에 유의하십시오.)
대안은 Rejected Ideas 섹션의 소스 호환성을 유지하면서 ABI를 완전히 분리하기를 참조하십시오.
태그 이름
이 ABI가 abi3와 유사하고 자유 스레딩을 지원하는 데 필요한 변경 사항이 최소화된다는 점을 반영하여 abi3t 태그를 선택했습니다. (자유 스레딩은 cp314t와 같은 기존 버전별 ABI 태그에서 문자 t를 사용합니다.)
대안은 Rejected Ideas 섹션의 이를 abi4로 명명하기를 참조하십시오.
파일 이름 태그
파일 이름에 abi3 태그를 사용하는 시스템에서는 새 파일 이름 태그(abi3t)를 추가하여 기존의 안정 ABI 확장(name.abi3.so)을 자유 스레드 Python용 안정 ABI를 지원하는 확장(name.abi3t.so)과 동일한 디렉터리에 설치할 수 있도록 합니다.
파일 이름에는 ABI 태그를 하나만 사용할 수 있습니다(휠 태그의 “압축된 태그 집합”과 같은 개념은 없습니다). 따라서 두 ABI 모두와 동시에 호환되는 확장은 태그 중 하나를 사용해야 하며, 기존 태그는 이미 의미가 있으므로 새 태그인 abi3t를 사용해야 합니다.
대안은 Rejected Ideas 섹션을 참조하십시오:
매크로 이름
이 PEP에서는 C 전처리기 매크로 Py_TARGET_ABI3T가 abi3t용 컴파일을 활성화한다고 규정합니다(실제로는 Python.h가 순방향 호환 정의만 노출하도록 합니다).
abi3에 대응하는 “매크로”의 이름은 Py_LIMITED_API입니다. 이 이름에는 문제가 있습니다.
- 이 이름은 매크로의 역사적인 내부 효과(어떤 정의를 노출할지 제한하는 것)는 설명하지만, 의도된 이점(순방향 호환성)은 설명하지 못합니다.
- 이 이름은 점점 더 잘못된 명칭이 되고 있습니다.
Py_TYPE와 같은 API의 경우 API를 제한하는 대신 순방향 호환 구현(인라인 포인터 역참조가 아닌 DLL 함수 호출)을 선택하기 때문입니다. - Stable ABI와 Limited API라는 두 용어의 조합은 기술적으로 정확하지만, 상당히 혼란스럽습니다. Limited API라는 용어를 피하고 “주어진 ABI를 대상으로 하는 데 필요한 제약 조건”이라고 표현하는 편이 더 명확합니다.
제안된 매크로 이름 (Py_TARGET_ABI3T)은 API 제한을 암묵적인 대가로, 전방 호환성을 암묵적인 이점으로 하여 abi3t를 컴파일 대상으로 강조합니다.
Py_LIMITED_API에 관해서는 이 PEP에서 변경 사항을 제안하지 않으며, 이는 abi3에 계속 사용하는 것을 의미합니다. abi3는 if 자유 스레드 빌드가 GIL이 활성화된 빌드를 대체하는 경우 결국 무의미해질 것으로 예상됩니다(잠정적인 계획은 PEP 703 acceptance notice를 참조하십시오). 그 시점에도 Py_LIMITED_API는 사용자에게 계속 표시되겠지만, 구현 세부 사항으로 남을 가능성이 높습니다.
대안으로 기존 “노브”를 재사용하는 방법은 Rejected Ideas 섹션을 참조하십시오: 새로운 ABI를 활성화하기 위해 Py_GIL_DISABLED 재사용하기
사양
자유 스레드 빌드를 위한 안정 ABI
Python은 자유 스레드 빌드를 위한 안정 ABI라고 하는 새로운 안정 ABI를 도입하며, 줄여서 abi3t라고 합니다. 현재의 안정 ABI(abi3)와 마찬가지로 abi3t는 Python 인터프리터의 주 버전(3)과 부 버전을 사용하여 버전이 지정됩니다. abi3t 3.x용으로 빌드된 확장은 3.x 이상 버전의 CPython 자유 스레드 빌드와 호환됩니다.
이는 기존 안정 ABI인 abi3에 대한 호환성 약속을 따릅니다. 이 약속은 PEP 384에서 정의되었고 PEP 703에서 수정되었습니다. abi3 3.x용으로 빌드된 확장은 GIL이 활성화된 CPython 3.x 이상 버전의 빌드와 호환됩니다.
abi3t용 C/C++ 확장을 빌드하려면 장기간 지원을 약속할 수 있는 API만 사용하도록 확장 자체를 제한해야 합니다. 이 자유 스레드 빌드를 위한 제한된 API는 3.15 Limited API의 하위 집합입니다.
abi3t용으로 컴파일된 모든 확장은 실제로 abi3와도 호환됩니다. 그러나 사용자와 도구가 둘 다 동시에 명시적으로 컴파일하고 이를 명시적으로 표시할 것을 권장합니다. (PyPA 패키징 생태계에서 이 표시는 아래에 설명된 휠 태그 abi3.abi3t를 사용하는 것을 의미합니다.)
대상 ABI 선택
C API 사용자 또는 사용자를 대신해 작동하고 도구별 UI를 통해 구성된 빌드 도구는 다음 매크로를 사용하여 대상 ABI를 선택합니다.
Py_LIMITED_API=<version>(기존): 주어진 버전의abi3용으로 컴파일합니다.Py_TARGET_ABI3T=<version>(여기서 제안): 주어진 버전의abi3t용으로 컴파일합니다.
Python.h는 각각 사용 편의성과 구현 단순성을 위해 다음 상황에서 구성 매크로를 자동으로 설정합니다.
- 관련
Py_LIMITED_API=v와Py_GIL_DISABLED가 설정되면Py_TARGET_ABI3T는 기본적으로v로 정의됩니다. (이를 통해 기존 매크로를 정의하고 자유 스레드 CPython 헤더로 컴파일하여abi3t를 선택할 수 있습니다. CPython 3.14에서는 이 경우 컴파일 시간 오류가 발생한다는 점에 유의하십시오.) - 관련
Py_TARGET_ABI3T=v가 설정되면 CPython은Py_LIMITED_API를v로 정의하거나 재정의할 수 있습니다. (이는 CPython이 사용 가능한 API를 선택하기 위해 내부적으로Py_LIMITED_API매크로를 계속 사용할 수 있음을 의미합니다.)
또한 Py_TARGET_ABI3T가 정의되어 있으면 Python.h는 Py_GIL_DISABLED도 정의되도록 보장합니다. 사용자는 이 매크로를 확인하여 추가 잠금과 같은 자유 스레딩별 코드를 활성화할 수 있습니다.
불투명한 PyObject
abi3t는 처음에는 abi3와 한 가지 차이만 갖습니다. 즉, PyObject 구조체와 이에 의존하는 API는 abi3t의 일부가 아닙니다.
구체적으로 abi3t용으로 빌드할 때 CPython 헤더는 다음을 수행합니다.
- 다음 구조체를 opaque(또는 C 용어로 incomplete types)로 만듭니다.
PyObjectPyVarObjectPyModuleDef_BasePyModuleDef
- 다음 매크로를 더 이상 포함하지 않습니다:
PyObject_HEAD_PyObject_EXTRA_INITPyObject_HEAD_INITPyObject_VAR_HEADPy_SET_TYPE()
일반 안정 ABI(abi3 3.15+)와 새로운 abi3t 모두에서 다음 항목은 매크로가 아니라 내보내지는 함수(ABI에 노출되는 함수)가 됩니다:
영향
PyObject, PyVarObject 및 PyModuleDef 구조체를 불투명하게 만든다는 것은 다음을 의미합니다:
- 해당 필드에 직접 액세스할 수 없습니다.
예를 들어
o->ob_type대신 확장 모듈은Py_TYPE(o)를 사용해야 합니다. 이러한 사용 방식은 한동안 권장되는 관행이었습니다. - 해당 크기와 정렬을 사용할 수 없게 됩니다.
sizeof(PyObject)같은 표현식은 더 이상 작동하지 않습니다. - 해당 구조체를 다른 구조체에 포함할 수 없습니다. 이는 주로 확장에서 정의된 타입의 인스턴스 구조체에 영향을 미치며, 이러한 구조체는 PEP 697에서 추가된 API를 사용하여 정의해야 합니다. 즉, 시작 부분에
PyObject(또는 다른 베이스 클래스 구조체)가 without 없이 있는struct를 사용하고, 메모리에 액세스하려면PyObject_GetTypeData()호출을 사용해야 합니다. - 이러한 형식의 변수를 생성할 수 없습니다. 이는 주로 확장 모듈을 정의하는 데 필요한 정적
PyModuleDef변수에 영향을 미칩니다. 사실상 모든 확장은abi3t를 지원하기 위해 PEP 793에 추가된 새로운 내보내기 후크(PyModExport_modulename())로 전환해야 합니다.
확장은 이러한 함수에 전달할 유효한 정적 할당 입력을 생성할 수 없으므로, 다음 함수는 abi3t에서 사실상 사용할 수 없게 됩니다. 그러나 해당 함수가 제거되지는 않습니다.
런타임 ABI 검사
사용자 대신 행동하는 빌드/설치 도구라고 하는 편이 더 정확하지만, 사용자는 호환되지 않는 확장을 Python의 임포트 경로에 넣지 않을 책임을 계속 집니다. 일반적으로 도구에는 CPython이 검사할 수 있는 것보다 훨씬 풍부한 메타데이터가 있으므로, 이러한 결정은 타당합니다. 일반적으로 빌드 도구와 설치 프로그램은 호환성 세부 정보를 전달하기 위해 PyPA packaging metadata와 platform compatibility tags를 사용하지만, 다른 모델도 가능합니다.
그러나 CPython은 기본 ABI 정보를 포함하는 새로운 module slot인 Py_mod_abi를 추가하여, 오래되었거나 잘못 구성된 도구 또는 사람의 실수에 대비한 방어선을 마련합니다. 모듈이 로드될 때 이 정보를 검사하며, 호환되지 않는 확장은 거부됩니다. 세부 사항은 C API 작업 그룹에 맡깁니다. (capi-workgroup issue 72를 참조하십시오. 이 이슈는 이 PEP가 최종 확정되기 훨씬 전에 구현되었습니다. 또한 PyABIInfo.flags에 abi3와 abi3t 모두와의 호환성을 나타내는 PyABIInfo_FREETHREADING_AGNOSTIC 플래그를 추가합니다.)
이 슬롯은 PEP 793에서 추가된 새로운 내보내기 훅과 함께 mandatory가 됩니다. (해당 PEP에는 현재 “필수 슬롯은 없습니다”라고 되어 있으며, 업데이트될 예정입니다.)
자유 스레딩 빌드에서 비자유 스레딩 ABI 검사
또한 자유 스레딩 빌드에서 PyModuleDef_Init()은 비자유 스레딩 Stable ABI를 사용하는 확장을 감지하고, 해당 확장이 로드될 때 정보를 제공하는 메시지를 출력하며, and 예외를 발생시킵니다. (구현 참고: 호환되지 않는 ABI를 사용하여 예외를 처리하려는 확장은 충돌하여 예외 메시지를 잃을 가능성이 높으므로, 예외를 발생시키기 전에 메시지를 출력합니다.)
비자유 스레딩 abi3를 검사하는 이 기능은 내부 비트 패턴에 의존하므로, 내부 객체 레이아웃을 변경해야 하는 경우 향후 CPython 버전에서 제거될 수 있습니다.
abi3t 휠 및 파일 이름 태그
Free-Threaded Python용 Stable ABI로 컴파일된 확장 모듈이 포함된 휠은 새로운 ABI tag인 abi3t를 사용해야 합니다.
Stable ABI 확장의 파일 이름이 .abi3.so로 끝나는 시스템에서는 자유 스레딩을 지원하는 확장이 대신 abi3t.so를 사용해야 합니다. 여기에는 abi3와 abi3t 모두와 호환되는 확장도 포함됩니다.
이러한 시스템에서는 CPython의 모든 빌드, 즉 GIL 사용 빌드와 자유 스레딩 빌드 모두 abi3t 태그가 있는 확장을 로드합니다. 자유 스레드 빌드는 3.14에서와 달리 abi3 태그가 있는 확장 기능을 로드하지 않습니다. 두 태그가 모두 있는 파일이 존재하면 GIL 사용 빌드는 *.abi3.so를 *.abi3t.so보다 우선합니다.
다시 말해 importlib.machinery.EXTENSION_SUFFIXES는 (CPython의 x86_64-linux-gnu 빌드에서) 다음과 같습니다.
python3.15:['.cpython-315-x86_64-linux-gnu.so', '.abi3.so', '.abi3t.so', '.so']python3.15t:['.cpython-315-x86_64-linux-gnu.so', '.abi3t.so', '.so']
GIL 사용 빌드가 .abi3t.so 파일을 로드하도록 하는 것은 순전히 실용적인 선택입니다. 이 한 가지 경우에는 abi3와 abi3t가 별개의 ABI라는 개념적 순수성을 깨뜨립니다.
설치 프로그램을 위한 권장 사항
패키지 설치 프로그램은 현재 (다른 조건이 동일한) 비자유 스레딩 빌드에 대해 abi3 태그가 있는 휠을 허용하는 모든 곳에서 자유 스레딩 빌드에 대해 abi3t 태그가 있는 휠도 허용해야 합니다.
이 PEP는 Free-threaded Python 3.14 (cp314-abi3t) 및 그 이전 버전을 대상으로 Stable ABI를 사용하는 방법을 제공하지 않는다는 점에 유의하십시오. 향후 또는 실험적 빌드 도구를 통해 이 점이 변경될 수 있으므로, 설치 프로그램은 이러한 확장에 대비해야 합니다.
빌드 도구를 위한 권장 사항
빌드 도구는 사용자에게 새로운 옵션을 제공해야 합니다. 기존 CPython 버전별 ABI(cp3nn)와 안정 ABI(abi3)를 대상으로 컴파일하는 것에 더해, 다음 방법 중 하나로 abi3와 abi3t 모두를 동시에 대상으로 확장을 컴파일할 수 있도록 해야 합니다:
- 관련
Py_LIMITED_API=v와Py_TARGET_ABI3T=v를 모두 정의하거나 - 관련
Py_LIMITED_API=v를 정의하고 다음을 수행하는 경우:- (Windows에서)
Py_GIL_DISABLED를 정의하거나 - (그 외의 경우) 자유 스레딩 CPython 헤더로 빌드합니다.
- (Windows에서)
위에서 v는 확장이 호환되어야 하는 가장 낮은 Python 버전을 Py_PACK_VERSION() 형식으로 나타냅니다. 이 버전은 3.15 이상이어야 합니다.
관련 PEP 3149에서 도입된 ABI 버전 태그가 지정된 .so 파일을 사용하는 시스템(Linux, macOS 및 이와 유사한 시스템)에서는 확장 모듈의 이름을 modulename.abi3t.so로 지정해야 합니다. 그 밖의 경우에는 변경 사항이 없어야 합니다. Windows에서는 name.pyd를 사용하십시오.
이러한 확장을 포함하는 휠에는 compressed ABI tag set인 abi3.abi3t와, 위의 v에 해당하는 Python tag cp3yy를 태그로 지정해야 합니다.
예를 들어 cp315-abi3.abi3t 태그가 지정된 휠은 3.15, 3.16 및 이후 버전과 호환되며, cp317-abi3.abi3t 태그가 지정된 휠은 3.17 이상과 호환됩니다.
권장되지는 않지만, 오직 abi3t와 호환되는 확장을 컴파일할 수 있습니다( Py_TARGET_ABI3T=v만 정의하고 결과 휠에 abi3.abi3t 대신 abi3t를 태그로 지정하면 됩니다). 이렇게 하면 결과가 자유 스레드 인터프리터로만 제한됩니다.
abi3t 확장을 CPython 3.14(또는 더 낮은 버전)와 호환되도록 빌드하는 것도 가능하지만, 이는 지원되지 않으며 제한 사항과 보장에 대한 상세한 이해와 철저한 테스트가 필요합니다.
새로운 API
이 PEP를 구현하면 자유 스레드 Python에서 성공적으로 로드할 수 있는 확장을 빌드할 수 있지만, 반드시 GIL 없이 스레드 안전한 확장을 빌드할 수 있는 것은 아닙니다.
GIL 없이 스레드 안전성을 허용하는 제한된 API는 C API 워킹 그룹을 통해 또는 후속 PEP에서 추가될 예정입니다. (참고: PyCriticalSection API는 C API WG issue 100에서 3.15에 추가되었습니다.)
하위 호환성 및 상위 호환성
abi3t를 대상으로 하는 확장은 PyModuleDef를 피하고 PEP 793에서 추가된 새로운 PyModExport 후크를 사용해야 하므로, 소스 수준(API)에서도 컴파일된 형식(ABI)에서도 이전 CPython 릴리스와 하위 호환되지 않습니다.
전환할 수 없는 확장 작성자는 기존 abi3를 계속 사용할 수 있습니다. 즉, Py_GIL_DISABLED를 정의하지 않고 GIL이 활성화된 Python에서 빌드할 수 있습니다. 자유 스레드 빌드와의 호환성을 위해 버전별 ABI를 사용하여 컴파일할 수 있습니다. 즉, Py_TARGET_ABI3T를 정의하지 않고 자유 스레드 CPython 빌드에서 abi3 호환 소스를 컴파일할 수 있습니다.
기존 안정 ABI의 경우에도 abi3 3.x는 PEP 384에서 약속되고 PEP 703에서 수정된 대로, GIL이 활성화된 CPython 3.x 이상과 계속 호환됩니다.
호환성 개요
다음 표는 휠 태그와 CPython 인터프리터의 호환성을 요약합니다. “GIL”은 GIL이 활성화된 인터프리터를, “FT”는 자유 스레드 인터프리터를 의미합니다.
| Wheel tag | 3.14 (GIL) | 3.14 (FT) | 3.15 (GIL) | 3.15 (FT) | 3.16+ (GIL) | 3.16+ (FT) |
|---|---|---|---|---|---|---|
cp314-cp314 |
✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
cp314-cp314t |
❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
cp314-abi3 |
✅ | ❌ | ✅ | ❌ | ✅ | ❌ |
cp314-abi3t (*) |
❌ | ✅ | ❌ | ✅ | ❌ | ✅ |
cp314-abi3.abi3t (*) |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
cp315-cp315 |
❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
cp315-cp315t |
❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
cp315-abi3 |
❌ | ❌ | ✅ | ❌ | ✅ | ❌ |
cp315-abi3t |
❌ | ❌ | ❌ | ✅ | ❌ | ✅ |
cp315-abi3.abi3t |
❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
(*): 이러한 태그가 지정된 휠은 빌드할 수 없습니다. 아래 표를 참조하십시오.
다음 표는 주어진 인터프리터로 빌드하고 지정된 매크로를 정의한 확장에 어떤 휠 태그를 사용해야 하는지 요약합니다.
| To get the wheel tag… | Compile on… | Py_LIMITED_API |
Py_TARGET_ABI3T |
Note |
|---|---|---|---|---|
cp314-cp314 |
3.14 (GIL) | — | N/A | existing |
cp314-cp314t |
3.14 (FT) | — | N/A | existing |
cp314-abi3 |
3.14+ (GIL) | 3.14 | N/A | existing |
cp314-abi3t |
N/A | reserved | ||
cp314-abi3.abi3t |
N/A | reserved | ||
cp315-cp315 |
3.15 (GIL) | — | — | continued |
cp315-cp315t |
3.15 (FT) | — | — | continued |
cp315-abi3 |
3.15+ (GIL) | 3.15 | — | continued |
cp315-abi3t |
3.15+ | — | 3.15 | new |
cp315-abi3.abi3t |
3.15+ (FT) | 3.15 | — | new |
| 3.15+ | 3.15 | 3.15 | ||
“Compile on” 열에서 FT는 Py_GIL_DISABLED 매크로가 명시적으로 정의되거나, Windows 이외의 플랫폼에서는 --disable-gil 옵션으로 구성된 CPython 헤더를 포함하여 반드시 정의되어야 함을 의미합니다. GIL은 Py_GIL_DISABLED 매크로가 정의되어서는 안 됨을 의미합니다. 이 주석이 없는 행은 두 경우 모두에 적용됩니다.
Py_LIMITED_API 및 Py_TARGET_ABI3T에서 대시는 매크로를 정의해서는 안 됨을 의미하며, 버전은 매크로를 Py_PACK_VERSION() 형식의 해당 정수로 설정해야 함을 의미합니다.
Note 열의 값:
- 기존: 휠 태그가 현재 사용되고 있습니다.
- 계속됨: 휠 태그가 기존 체계를 계속 사용합니다.
- 새로 제안됨: 이 PEP에서 제안됩니다.
- 예약됨: 이 PEP에서는 일치하는 확장을 빌드하는 메커니즘을 제안하지 않지만, 향후 추가될 수 있습니다. 설치 도구는 해당 태그를 처리할 수 있도록 준비되어야 합니다.
보안 관련 사항
알려진 사항이 없습니다.
이 내용을 가르치는 방법
포팅 가이드에서는 PEP 697에 추가된 API(불투명 타입 확장을 위한 제한된 C API)와 PEP 793(PyModExport)로 어떻게 전환하는지 설명해야 합니다.
참조 구현
이 PEP는 개별적으로 구현된 여러 부분을 결합합니다.
_Py_OPAQUE_PYOBJECT매크로를 정의하면 CPython 메인 브랜치에서 불투명한PyObject을 사용할 수 있습니다. GitHub 풀 리퀘스트 python/cpython#136505에서 구현되었습니다.PyModExport에 대해서는 PEP 793과 GitHub issue #140550를 참조하십시오.- 버전 확인 슬롯은 GitHub 풀 리퀘스트 python/cpython#137212에서 구현되었습니다.
- 이전
abi3에 대한 검사는 GitHub 풀 리퀘스트 python/cpython#137957에서 구현되었습니다. packaging프로젝트는 pypa/packaging/pull#1099에서 설치 프로그램의 휠 태그 처리를 구현했습니다.- 빌드 도구에 대해서는 여러 개별 풀 리퀘스트가 제출되었으므로, 자세한 내용은 Nathan에게 문의하십시오.
이 PEP가 승인된 후 구현은 CPython issue 146636에서 추적되었습니다.
거부된 아이디어
단일 새 ABI: Stable ABI 3.15에서 PyObject를 불투명하게 만들기
Stable ABI에 새로운 변형을 도입하는 대신 Stable ABI에서 PyObject 구조체를 불투명하게 만들 수 있습니다.
이는 확장 모듈 작성자가 새로운 제한 사항에 맞게 코드를 수정하거나, Python 3.15에서 도입되는 C API를 사용하기 위해 Stable ABI를 완전히 포기해야 한다는 의미입니다.
또한 새 휠 태그(abi3t)의 필요성이 완전히 사라지는 것도 아니며, 이 태그는 CPython 3.14 이하의 GIL 사용 빌드와 자유 스레드 빌드 모두와 호환되는 확장 모듈임을 나타내기 위해 필요합니다.
PEP discussion에서는 소스 변경 없이 GIL 전용 Stable ABI용으로 빌드할 수 있는 기능이 추가 구성 매크로를 사용할 만한 가치가 있다고 판단되었으며, 이 매크로는 현재 Py_TARGET_ABI3T라고 합니다.
CPython 3.14와의 호환성을 위한 심
3.14와 호환되는(자유 스레드 빌드와 기본 빌드 모두) cp314-abi3.abi3t 확장 모듈을 빌드할 수 있습니다. 이와 관련된 과제는 다음과 같습니다.
- 일반적인 확장 모듈에서 편리하고 안전하게 만들기
- 이를 테스트하기(CPython의 테스트 모음은 테스트 중인 버전 이외의 CPython 버전을 포함하지 않기 때문입니다)
따라서 이러한 확장 모듈을 빌드하는 메커니즘을 제공하는 일은 외부 프로젝트(예를 들어 pythoncapi-compat와 같은 프로젝트)에 맡기는 것이 가장 적합합니다. 이는 CPython의 C API와 이 PEP의 범위에 포함되지 않습니다.
이러한 메커니즘이 어떻게 작동할 수 있는지 개략적으로 설명하면 다음과 같습니다.
Python 3.14와의 호환성을 막는 주요 문제는 불투명한 PyObject와 PyModuleDef를 사용하는 경우 확장 모듈을 초기화할 수 없다는 점입니다. 해결책인 PEP 793은 Python 3.15에서만 추가됩니다.
3.14의 ABI(자유 스레드와 GIL 사용 모두)가 “동결”되어 있다는 사실을 이용하면 이 문제를 우회할 수 있으므로, 확장 모듈이 실행 중인 인터프리터를 조회하고 3.14에서는 감지된 빌드의 PyModuleDef에 해당하는 struct 정의를 사용할 수 있습니다.
abi3t를 abi3와 호환되게 만들기
패키징 도구에 abi3t가 abi3의 “부분 집합”이라고 가르칠 수 있으며, 즉 GIL이 활성화된 모든 인터프리터가 abi3t로 태그된 빌드와 호환된다고 보장할 수 있습니다. 이렇게 하면 abi3.abi3t 압축 태그 집합이 abi3t와 동등해져 불필요해집니다. 그러나 도구는 abi3t를 GIL 활성화 빌드와 호환되는 것으로 간주하지 않는 “이전 설치 프로그램”을 지원하려면 압축된 태그 집합을 계속 출력해야 합니다.
여기서 “이전 설치 관리자”에는 abi3t에 맞게 업데이트되지 않은 packaging 라이브러리 버전을 사용하거나 포함하는 설치 관리자가 포함됩니다. (packaging 라이브러리는 Python 기반 설치 관리자가 휠 태그 매칭을 구현하는 데 일반적으로 사용하는 라이브러리입니다.)
설치 관리자 외에도 abi3.abi3t 태그를 사용하면 PyPI 파일 목록의 ABI 필터와 같은 메커니즘이 별도의 특수 처리 없이 abi3를 매칭할 수 있습니다(압축 태그가 표준에 따라 처리된다는 전제하에). 예를 들어 https://pypi.org/project/cryptography/#files에서 사용할 수 있습니다. abi3와의 호환성을 궁금해하는 사용자에게도 더 명확한 신호를 제공합니다.
덜 중요하지만 ABI를 병합하면 향후 abi3와 abi3t가 서로 달라질 가능성을 열어 두는 “비상구”도 제거됩니다.
이를 abi4로 명명하기
abi3t 대신 “버전을 올려” abi4를 사용할 수도 있습니다. 차이는 대체로 외관상의 차이입니다.
abi4 태그를 추가한다면 옵트인 매크로의 값(Py_TARGET_ABI4 또는 Py_LIMITED_API 등)은 다음 중 하나여야 합니다:
abi4에 맞게4로 시작하도록 변경하되, 더 이상PY_VERSION_HEX에 대응하지 않게 하거나(생성 및 검사가 더 어려워짐),- 변경하지 않아
abi4와 일관되지 않게 하거나.
abi3t를 추가하는 것은 abi4를 추가하는 것보다 작은 변경이므로, PEP 809의 abi2026과 같은 더 큰 변경에 앞선 과도기적 상태로 더 적합합니다.
abi3+abi3t 파일 이름 태그
파일 이름 ABI 태그는(PEP 3149에서 도입됨) 여러 ABI용 확장이 하나의 디렉터리에 공존할 수 있도록 합니다.
이 PEP에 따르면 abi3와 abi3t 모두와 호환되는 확장은 휠 메타데이터에서 압축 태그 집합(abi3.abi3t)을 사용하지만, 파일 이름에서는 사용하지 않습니다(.abi3.so/.abi3t.so). 조합을 위한 전용 태그를 추가할 수도 있습니다 – 예를 들어 .abi3+abi3t.so와 같습니다.
그러나 .abi3+abi3t.so 확장이 .abi3t.so 확장과 공존할 필요는 없습니다. 자유 스레드 인터프리터는 항상 .abi3t.so를 선택하므로 GIL이 활성화된 인터프리터용 확장도 .abi3.so를 사용하면 되기 때문입니다. 유일한 이점은 abi3.abi3t 확장이 abi3 전용 동등 확장과 함께 설치되지 않은 경우 더 명확한 이름을 사용할 수 있다는 점입니다.
여기서는 더 명확한 이름을 얻기 위해 복잡성을 감수할 가치가 없습니다. 실용적인 선택으로 .abi3t.so가 “abi3+abi3t”, 즉 “모든 빌드에서 로드 가능함”을 의미하도록 합니다. 이는 (권장되지 않는) abi3t 전용 확장에서도 작동합니다. GIL이 활성화된 인터프리터에서는 이러한 확장이 필수 runtime ABI check를 통과하지 못하며, abi3t와 abi3가 서로 달라지는 가능성 낮은 미래에는 링커 심볼이 없어 로드에 실패할 수도 있습니다.
개념적으로 파일 이름 태그는 확장의 ABI를 “설명”하거나 “명명”하지 않습니다. 현재의 .abi3 태그는 버전 정보가 없으므로 이미 이 목적에 비해 너무 불충분합니다.
파일 이름 태그 없음(일반 .so)
파일 이름 ABI 태그를 완전히 제거하고, .abi3t.so 대신 .so를 사용할 수도 있습니다. 이 두 태그의 실질적인 의미는 매우 비슷합니다. .so는 CPython의 모든 빌드에서 로드할 수 있고, .abi3t.so는 3.15 이상 버전의 CPython에서 로드할 수 있습니다. 다만 Stable ABI 파일 이름 태그에는 이미 버전 정보가 없습니다.
하지만 의미론적으로는 서로 다릅니다. 그러나 의미상으로는 서로 다릅니다. 단순한 .so는 “상관하지 않음”을 의미하며, .abi3t.so 확장은 새로운 ABI와 의도적으로 호환됩니다.
새로운 ABI를 활성화하기 위해 Py_GIL_DISABLED 재사용하기
Py_GIL_DISABLED 매크로가 Py_LIMITED_API와 함께 정의된 경우 abi3 대신 abi3t를 선택하도록 할 수 있습니다.
이는 빌드 플래그를 성가시게 조작해야 하며, abi3와 abi3t를 동시에 명시적으로 대상으로 지정하는 것을 불가능하게 합니다.
소스 호환성을 유지하면서 ABI를 완전히 분리하기
abi3와 abi3t를 완전히 분리하여 서로 호환되지 않게 만들 수 있습니다. 이를 통해 현재의 모든 확장 기능이 abi3t와 소스 호환성을 유지할 수 있습니다. PyObject 구조체를 계속 노출된 상태로 둘 수 있으며, 각 ABI가 버전별 CPython ABI에서처럼 서로 다른 비공개 필드 집합을 정의하도록 할 수 있습니다.
그러나 노출된 PyObject 구조체는 기존 Stable ABI의 주요 단점 중 하나로 지적되어 왔습니다. 이는 불멸화 및 프리 스레딩 자체와 같은 최적화와 기능을 방해하거나 불가능하게 만들었습니다.
PyObject를 노출하는 것은 이 실수를 반복하고, 현재의 프리 스레드 정의를 “동결”하며, 변경이 필요할 경우 또 다른 안정 ABI 변형을 요구하는 것을 의미합니다.
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.