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 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
안정 ABI의 새로운 변형인 “자유 스레드 Python을 위한 안정 ABI”(줄여서 abi3t)를 추가합니다.
abi3t는 기존 안정 ABI(abi3)를 기반으로 하지만, PyObject 구조체를 불투명하게 만듭니다. 이에 따라 사용자는 모듈과 대부분의 클래스를 정의하는 것과 같은 일반적인 작업에서 새로운 API로 이전해야 합니다.
실제로 abi3t 3.15는 abi3 3.15와 호환됩니다. 확장 작성자는 두 ABI를 대상으로 한 번에 명시적으로 컴파일하고, 휠 태그 abi3.abi3t를 사용하여 호환성을 표시하는 것이 좋습니다.
용어
이 PEP에서는 “GIL 활성화 빌드”를 “자유 스레드 빌드”의 반대말로, 즉 Py_GIL_DISABLED 없이 빌드된 인터프리터나 확장을 가리키는 말로 사용합니다.
동기
현재 자유 스레드 빌드에서는 안정 ABI를 사용할 수 없습니다. 자유 스레드 Python에서(즉, Py_LIMITED_API와 Py_GIL_DISABLED 전처리기 매크로가 모두 정의된 경우) 확장은 안정 ABI용으로 빌드되지 않습니다. CPython의 GIL 활성화 빌드용으로 빌드된 확장은 자유 스레드 빌드에서 로드에 실패합니다(또는 크래시가 발생합니다).
운영 위원회는 PEP 779에 대한 수락 게시물에서 “자유 스레딩을 위한 안정 ABI가 파이썬 3.15에 맞춰 준비되고 정의될 것으로 기대합니다”라고 밝혔습니다.
이 PEP는 자유 스레딩을 위한 안정 ABI를 제안합니다.
배경 및 요약
PEP 384와 PEP 652에 정의된 파이썬의 안정 ABI(줄여서 abi3)는 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는 CPython 3.15+의 GIL 활성화 빌드와 자유 스레드 빌드 모두와 호환되는 확장을 컴파일하는 데 필요한 추가적인 API 제한, 이러한 제한을 선택적으로 적용하기 위한 대응 매크로, 그리고 확장이 이러한 호환성을 알리는 데 사용해야 하는 명명/태깅 체계를 포함하는 자유 스레딩 빌드를 위한 안정 ABI (줄여서 abi3t)를 제안합니다.
생태계 유지 관리자는 유지 관리 부담이 줄어들기를 원합니다
제한된 API와 안정 ABI 휠의 주요 장점은 새 Python 버전이 출시 당일부터 지원된다는 것입니다. 안정 ABI 휠이 없으면 유지 관리자는 CPython 릴리스를 면밀히 따라가며 CPython 베타 기간 동안 휠을 만들어 내는 것과, 새 CPython 버전 지원에 대한 불가피한 사용자 요청에 대응하는 것 중 하나를 선택해야 합니다.
Cryptography
cryptography 프로젝트는 가장 최근 릴리스에서 48개의 wheel 파일을 배포했습니다. Cryptography는 새로운 제한된 API 버전에서 사용할 수 있는 최적화를 활용하기 위해 cp38과 cp311 안정 ABI 각각에 대해 14개의 휠을 배포한다는 점에서 다소 특이합니다. 또한 cp314t 휠 14개와 PyPy용 휠 6개를 추가로 배포합니다. 자유 스레드 안정 ABI가 없다면, Python 3.15에서 cryptography는 자유 스레드 빌드의 두 버전을 지원하기 위해 GIL 활성화 빌드의 EOL이 아닌 모든 버전과 거의 같은 양의 PyPI 공간을 사용하게 됩니다.
Cryptography 유지 관리자 Alex Gaynor는 Discourse에서 자유 스레드 안정 ABI에 대한 바람을 표명했습니다:
PyCA 유지 관리자 관점에서 명시적으로 말씀드리자면, O(1) 빌드라면 괜찮습니다. 저희가 할 수 없고 하지도 않을 것은 Python 릴리스마다 새 빌드가 필요한 O(n) 방식입니다.
PEP 작성자 중 한 명이 #pyca Libera IRC 채널에서 Alex에게 현재 의견을 묻자, 그는 다음과 같이 말했습니다:
abi3의 정말 가치 있는 점을 한 가지 더 말씀드리자면, 기존 휠이 새 Python 버전에서도 계속 동작한다는 것입니다. Python 릴리스별 휠을 만들어야 한다면, Python 릴리스 주기의 여러 시점에 상당한 작업을 해야 합니다(그 시점에 따로 릴리스를 계획하고 있지 않다면, 새 휠을 추가하기 위한 백포트 릴리스를 내야 할 수도 있습니다).유지 관리자로서 저희는 그런 식으로 “시간에 쫓기는” 일을 피하도록 작업을 구성하는 것을 정말 선호합니다.
moocore
moocore 프로젝트는 abi3 휠 7개를 배포합니다. 자유 스레드 빌드 지원을 추가하는 주제가 moocore 이슈 트래커에서 제기되었을 때, 유지 관리자 Manuel López-Ibáñez는 이슈를 보고한 사람에게 다음과 같이 알렸습니다:
정말 필요하지 않다면 3.14 자유 스레딩용으로는 빌드하고 싶지 않습니다.
이후 자유 스레드 빌드에서 제한된 API를 지원하기 위한 추적 이슈를 발견한 뒤, 그는 다음과 같이 댓글을 남겼습니다:
참고로, python/cpython#111506은 자유 스레드 Python을 지원하도록 안정 ABI를 확장하는 것에 관한 이슈입니다. 그렇게 된다면, Python 버전마다 새 휠을 빌드할 필요 없이 moocore 빌드가 기존 Python과 자유 스레드 Python 버전 모두에서 동작할 것입니다.[…]
Python 3.15가 릴리스되면 이 문제를 다시 검토하겠습니다. 그때는 ABI가 안정화되어 있기를 바랍니다(더 좋게는 자유 스레딩이 기본값이 되어 있기를 바랍니다).
Pydantic
Pydantic 유지 관리자 David Hewitt는 다음과 같이 언급했습니다:
Pydantic은 Rust와 PyO3로 빌드한 네이티브 코어의 휠을 배포합니다. pydantic-core의 최신 릴리스는 112개의 휠을 배포했으며, 더 많은 환경(예: Android, iOS, wasm)이 추가됨에 따라 이 수는 계속 늘어날 예정입니다. Pydantic은 그동안 기능 세트가 너무 미성숙했기 때문에 안정 ABI를 사용해 배포하지 않았습니다. Pydantic의 많은 기능이 핫 루프 안에서 C API를 통해 Python 객체와 상호 작용하므로 성능이 핵심 관심사입니다. 안정 ABI가 성숙해지면 Pydantic이 티어 2 플랫폼을 안정 ABI로 전환하는 것(그리고 어쩌면 결국 티어 1 플랫폼까지 전환하는 것)이 이상적이며, 이를 통해 빌드, 테스트, 배포해야 하는 휠의 수를 크게 줄일 수 있을 것입니다.자유 스레딩이 안정 ABI를 채택하지 않는다면, 자유 스레딩이 기본이자 유일한 빌드가 될 때(장기적으로 예상되는 계획으로 보입니다) 위의 모든 이점을 잃게 된다는 점을 강조하고 싶습니다.
SciPy
SciPy 프로젝트는 서로 다른 네 가지 CPython 버전을 지원하기 위해 지난 릴리스에 60개의 버전별 wheel 파일을 업로드했습니다. PyPy용 휠은 업로드하지 않습니다.
이 제안에 대한 질문을 받자, SciPy 운영 위원회 의장 Ralf Gommers는 다음과 같이 말했습니다:
SciPy를 비롯한 과학 Python 생태계의 여러 프로젝트는 안정 ABI 사용을 시작하는 데 상당한 관심을 가지고 있으며, 특히 더 많은 휠을 제공하는 데 따르는 유지 관리 부담을 줄이고자 합니다. 최근의 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는 이 제안을 지지하며 다음과 같이 말했습니다:
PyO3는 안정 ABI가 있다는 점에서 큰 혜택을 받습니다. 이 프레임워크가 겪는 가장 큰 과제 중 하나는 매우 다양한 Python / OS / CPU / 환경 조합을 추상화해야 한다는 점입니다. 저희는 이러한 각 환경에 대해 안정 ABI로 빌드할 수 있는 선택지도 제공합니다(지정된 최소 Python 버전의 안정 ABI를 대상으로 합니다). 목표는 언제나 PyO3가 제공하는 모든 기능이 이 모든 조합에서 똑같이 동작하는 것입니다(특정 기능을 사용하려면 최소 Python 버전이 요구되는 경우도 있습니다). 현재 저희는 Python 3.7 이상을 지원합니다. 안정 ABI에 추가되는 모든 기능은 PyO3가 특정 기능을 지원하기 위해 조건부 코드를 더 도입할 필요가 없다는 매우 반가운 약속입니다. 장기적으로 이는 오래된 Python 버전에 대한 지원을 중단해 나가면서 PyO3가 코드 경로를 단순화할 수 있게 해 주며, 유지 보수 부담을 통제하는 데 도움이 됩니다.
이 제안에 대한 의견을 요청받았을 때, Cython 유지 관리자 David Woods는 다음과 같이 말했습니다:
Cython은 결국 순수 Python으로도 잘 동작하기 때문에, 저희가 배포하는 휠의 개수와 관련해 큰 문제가 없습니다. 몇몇 소규모 플랫폼용 휠을 안정 ABI 휠로 배포하고 있기는 하지만, 이는 실제로 필요해서라기보다는 “도그푸딩”에 가깝습니다. 그러니 저는 제가 유용하게 쓸 것이라서가 아니라 다른 사람들이 유용하게 여길 것을 기대하며 이것을 추가하는 것입니다.다만 이에 따르는 성능상의 트레이드오프가 많은 Cython 사용자에게 지나치게 클 것이라는 우려가 여전히 조금 남아 있습니다(다른 바인딩 도구에서는 트레이드오프가 다를 수도 있습니다). 일반 컴파일 모드를 없애는 것이 아니므로 사람들은 각자 원하는 트레이드오프를 자유롭게 선택할 수 있고, 따라서 이것이 큰 재앙은 아닙니다.
이 PEP가 몇 가지 질문과 작업을 미해결 상태로 남겨 둔다는 점에 유의해야 합니다. C++ 바인딩 제너레이터인 nanobind의 유지 관리자 Wenzel Jakob은 이 PEP의 범위에서 제외된 추가 API가 필요하다고 언급했습니다:
[PyVarObject에서 파생된 객체]의 N번째 데이터 항목에 대한 포인터를 어떻게 얻을 수 있는지 명확하지 않습니다.그 문제가 해결된다면 nanobind도 이 새로운
abi3t컴파일 대상을 도입할 수 있을 것입니다.
근거
abi3t의 설계에는 몇 가지 선택/가정/제약이 포함됩니다:
별도의 ABI
새로운 ABI(abi3t)는 개념적으로 기존 안정 ABI(abi3)와 별개로 취급됩니다. 실제로는 abi3t와 호환되는 모든 확장이 abi3와도 역시 호환되지만 그렇습니다.
(더 정확히 말하면, abi3t가 허용하는 API 집합은 abi3의 부분집합이 되고, abi3t와 호환되는 인터프리터 집합은 abi3의 상위집합이 됩니다. 이는 머리를 어지럽게 만들며, 이것이 둘을 별개로 유지하는 이유 중 하나입니다.)
확장은 abi3t와 abi3 둘 다를 대상으로 명시적으로 컴파일되어야 하며, 패키징 wheel 태그(abi3.abi3t)와 런타임 ABI 검사(Py_mod_abi 매크로)를 통해 두 가지를 동시에 지원한다는 것을 명시적으로 알려야 합니다.
이러한 명시성은 abi3t 지원이 abi3 지원을 암시하도록 하는 방식에 비해 여러 가지 장점이 있습니다:
- 태그를 보면 확장이 GIL 활성화 빌드와 호환되는지, 그리고
abi3의 기존 하위 호환성 보장이 적용되는지를 분명히 알 수 있습니다. - 구현 측면에서 보면, 특정 자유 스레드 인터프리터가 지원하는 태그 집합(packaging 함수
packaging.tags.sys_tags가 반환하는 집합)의 크기는 이에 대응하는 GIL 활성화 빌드의 태그 집합과 같습니다. - 이렇게 하면 향후 두 ABI가 조금씩 달라질 여지가 생깁니다 – 두 ABI를 동시에 지원하는 것을 선호되는 컴파일 대상으로 유지하면서도, 특수한 경우에는
abi3t전용 확장을 허용합니다.
ABI를 개념적으로 분리한다는 원칙에는 실질적인 예외가 하나 있으며, 이는 파일 이름 태그 절에서 다룹니다.
대안에 대한 자세한 내용은 다음 거부된 아이디어 절들을 참조하십시오:
현재로서는 하위 호환성 없음
CPython 헤더는 CPython 3.14 이하에서 abi3t용으로 컴파일하는 것을 허용하지 않습니다. 이것이 필요한 프로젝트는 3.14 자유 스레드 인터프리터 전용 확장과 구버전 abi3용 확장을 따로 빌드할 수 있습니다.
그러나 CPython 3.14 이상의 자유 스레드 빌드와 GIL 활성화 빌드 모두와 호환되는 확장을 빌드하는 것은 기술적으로 가능합니다. 이 영역에서 실험할 수 있도록, 패키지 설치 도구가 이러한 확장을 처리할 준비를 갖출 것을 권장합니다. 자세한 내용은 거부된 아이디어를 참조하십시오.
확장의 소스 변경이 필요함
abi3t를 사용하려면 확장 작성자가 코드를 상당히 변경해야 합니다.
(아직) 이렇게 할 수 없는 프로젝트는 abi3를 계속 사용하면서, 같은 소스를 자유 스레드 빌드의 특정 버전용으로 컴파일할 수 있습니다. (abi3t에서 제거된 API는 3.15t를 포함하여 특정 버전용으로 컴파일할 때는 여전히 사용할 수 있다는 점에 유의하십시오.)
대안에 대해서는 다음 거부된 아이디어 절을 참조하십시오: 소스 호환성을 유지하는 완전히 분리된 ABI
태그 이름
abi3t 태그는 이 ABI가 abi3와 유사하며, 자유 스레딩 지원에 필요한 최소한의 변경만 가해졌다는 사실을 반영하기 위해 선택되었습니다(자유 스레딩은 cp314t 같은 기존의 버전별 ABI 태그에서 t라는 문자를 사용합니다).
대안에 대해서는 다음 거부된 아이디어 절을 참조하십시오: 이름을 abi4로 정하기
파일 이름 태그
파일 이름에 abi3 태그를 사용하는 시스템에서는, 이전 안정 ABI 확장(name.abi3.so)을 자유 스레드 Python을 위한 안정 ABI를 지원하는 확장(name.abi3t.so)과 같은 디렉터리에 설치할 수 있도록 새로운 파일 이름 태그(abi3t)가 추가됩니다.
파일 이름에는 ABI 태그가 하나만 들어갈 수 있으므로(휠 태그에서와 같은 “압축된 태그 집합” 개념이 없습니다), 두 ABI와 동시에 호환되는 확장은 태그 중 하나를 사용해야 합니다 – 기존 태그는 이미 정해진 의미가 있으므로 새 태그(abi3t)를 사용해야 합니다.
대안에 대해서는 다음 거부된 아이디어 절들을 참조하십시오:
노브 이름
이 PEP는 C 전처리기 매크로 Py_TARGET_ABI3T가 abi3t용 컴파일을 활성화하도록 규정합니다(즉, 실질적으로는 Python.h가 전방 호환되는 정의만 노출하도록 합니다).
abi3에 대응하는 “노브”의 이름은 Py_LIMITED_API입니다. 이 이름에는 문제가 있습니다:
- 이 이름은 매크로가 역사적으로 내부에서 해 온 일(어떤 정의를 노출할지 제한하는 것)은 설명하지만, 의도된 이점(전방 호환성)은 설명하지 않습니다.
- 이 이름은 점점 더 실제와 맞지 않게 되고 있습니다:
Py_TYPE같은 API의 경우, 이 매크로는 API를 제한하는 대신 전방 호환되는 구현(인라인 포인터 역참조 대신 DLL 함수 호출)을 선택합니다. - 안정 ABI와 제한된 API라는 두 용어는 기술적으로 정확하지만 상당히 혼란스럽습니다. 제한된 API라는 용어를 피하고 “주어진 ABI를 대상으로 하는 데 필요한 제약 조건”에 대해 이야기하는 것이 대체로 더 명확합니다.
제안된 매크로 이름(Py_TARGET_ABI3T)은 abi3t를 컴파일 대상으로 강조합니다. API 제한은 그 암묵적인 대가이고, 전방 호환성은 암묵적인 이점입니다.
Py_LIMITED_API에 대해서는 이 PEP가 아무런 변경도 제안하지 않으며, 이는 이 매크로를 abi3용으로 그대로 유지한다는 뜻입니다. 자유 스레드 빌드가 GIL이 활성화된 빌드를 대체한다면 abi3는 결국 무의미해질 것으로 예상됩니다(잠정적인 계획은 PEP 703 수락 공지를 참조하십시오). 그 시점에도 Py_LIMITED_API는 사용자에게 계속 보이겠지만, 구현 세부 사항으로 남을 가능성이 큽니다.
기존 “조절 장치”를 재사용하는 대안은 거부된 아이디어 절을 참조하십시오: 새 ABI를 활성화하는 데 Py_GIL_DISABLED 재사용하기
명세
자유 스레드 빌드를 위한 안정 ABI
Python은 자유 스레딩 빌드를 위한 안정 ABI, 줄여서 abi3t라는 새로운 안정 ABI를 도입할 것입니다. 현재의 안정 ABI(abi3)와 마찬가지로 abi3t는 Python 인터프리터의 주 버전(3)과 부 버전을 사용하여 버전이 매겨집니다. abi3t 3.x용으로 빌드된 확장은 CPython 3.x 이상의 자유 스레딩 빌드와 호환됩니다.
이는 PEP 384에서 정의되고 PEP 703에서 수정된 기존 안정 ABI인 abi3의 호환성 약속을 그대로 따릅니다: abi3 3.x용으로 빌드된 확장은 CPython 3.x 이상의 GIL이 활성화된 빌드와 호환됩니다.
abi3t용 C/C++ 확장을 빌드하려면, 확장은 장기 지원을 약속할 수 있는 API만 사용하도록 스스로를 제한해야 합니다. 이 자유 스레드 빌드를 위한 제한된 API는 3.15 제한된 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 헤더는 다음을 수행합니다:
- 다음 구조체를 불투명하게(C 용어로는 불완전 타입으로) 만듭니다:
PyObjectPyVarObjectPyModuleDef_BasePyModuleDef
- 다음 매크로를 더 이상 포함하지 않습니다:
PyObject_HEAD매크로_PyObject_EXTRA_INIT매크로PyObject_HEAD_INIT매크로PyObject_VAR_HEAD매크로Py_SET_TYPE()
일반 안정 ABI(abi3 3.15+)와 새로운 abi3t 모두에서 다음 항목은 매크로가 아니라 (ABI에 노출되는) 내보내기 함수가 됩니다:
영향
PyObject, PyVarObject, PyModuleDef 구조체를 불투명하게 만든다는 것은 다음을 의미합니다:
- 이 구조체들의 필드에 직접 접근할 수 없습니다.
예를 들어, 확장은
o->ob_type대신Py_TYPE(o)를 사용해야 합니다. 이 방식은 이미 한동안 권장되어 온 관행입니다. - 이 구조체들의 크기와 정렬을 알 수 없게 됩니다.
sizeof(PyObject)와 같은 표현식은 더 이상 작동하지 않습니다. - 이 구조체들을 다른 구조체 안에 포함할 수 없습니다. 이는 주로 확장에서 정의한 타입의 인스턴스 구조체에 영향을 주며, 이러한 구조체는 PEP 697에서 추가된 API를 사용해 정의해야 합니다. 즉, 시작 부분에
PyObject(또는 다른 베이스 클래스 구조체)가 없는struct를 사용하고, 메모리에 접근하려면PyObject_GetTypeData()호출이 필요합니다. - 이 타입들의 변수를 만들 수 없습니다. 이는 주로 확장 모듈을 정의하는 데 필요한 정적
PyModuleDef변수에 영향을 줍니다. 사실상 모든 확장은abi3t를 지원하기 위해 PEP 793에서 추가된 새로운 내보내기 훅(PyModExport_modulename())으로 전환해야 합니다.
확장이 이 함수들에 전달할 유효한 정적 할당 입력을 만들 수 없으므로, 다음 함수들은 abi3t에서 사실상 사용할 수 없게 됩니다. 하지만 이 함수들이 제거되지는 않습니다.
런타임 ABI 검사
사용자는 – 정확히는 사용자를 대신해 동작하는 빌드/설치 도구는 – 앞으로도 호환되지 않는 확장을 Python의 임포트 경로에 두지 않을 책임을 집니다. 도구는 일반적으로 CPython이 검사할 수 있는 것보다 훨씬 풍부한 메타데이터를 가지고 있으므로, 이러한 결정은 타당합니다. 일반적으로 빌드 도구와 설치 프로그램은 PyPA packaging metadata와 platform compatibility tags를 사용하여 호환성 세부 정보를 전달하지만, 다른 모델도 가능합니다.
하지만 CPython은 오래되었거나 잘못 구성된 도구, 또는 사람의 실수에 대비한 방어선을 추가합니다. 이 방어선은 기본적인 ABI 정보를 담은 새 모듈 슬롯인 Py_mod_abi의 형태를 띱니다. 이 정보는 모듈이 로드될 때 검사되며, 호환되지 않는 확장은 거부됩니다. 구체적인 사항은 C API 작업 그룹에 맡깁니다. (capi-workgroup issue 72를 참조하십시오. 이 이슈는 이 PEP가 확정되기 훨씬 전에 구현되었습니다. 또한 abi3와 abi3t 모두와 호환됨을 나타내기 위해 PyABIInfo.flags에 쓰는 PyABIInfo_FREETHREADING_AGNOSTIC 플래그가 추가됩니다.)
이 슬롯은 PEP 793에서 추가되는 새 익스포트 훅과 함께 필수가 됩니다. (해당 PEP에는 현재 “필수 슬롯이 없습니다”라고 되어 있으며, 이 부분은 갱신될 예정입니다.)
자유 스레딩 빌드에서 비자유 스레드 ABI 검사
또한 자유 스레드 빌드에서 PyModuleDef_Init()는 비자유 스레딩 안정 ABI를 사용하는 확장을 감지하여, 그러한 확장이 로드될 때 안내 메시지를 출력하고 동시에 예외를 발생시킵니다. (구현 참고: 호환되지 않는 ABI를 사용해 예외를 처리하려는 확장은 크래시를 일으켜 예외 메시지를 잃어버릴 가능성이 높으므로, 예외를 발생시키기 전에 메시지를 먼저 출력합니다.)
비자유 스레딩 abi3에 대한 이 검사는 내부 비트 패턴에 의존하므로, 내부 객체 레이아웃을 변경해야 할 경우 향후 CPython 버전에서 제거될 수 있습니다.
abi3t 휠 태그와 파일 이름 태그
자유 스레드 Python용 안정 ABI로 컴파일된 확장 모듈을 포함하는 휠은 새 ABI tag인 abi3t를 사용해야 합니다.
안정 ABI 확장의 파일 이름이 .abi3.so로 끝나는 시스템에서는, 자유 스레딩을 지원하는 확장이 대신 abi3t.so를 사용해야 합니다. 여기에는 abi3와 abi3t 모두와 호환되는 확장도 포함됩니다.
이러한 시스템에서는 GIL이 활성화된 빌드와 자유 스레드 빌드를 포함한 모든 CPython 빌드가 abi3t 태그가 붙은 확장을 로드합니다. 자유 스레드 빌드는 3.14와 달리 abi3 태그가 붙은 확장을 로드하지 않습니다. 두 태그의 파일이 모두 있으면, GIL이 활성화된 빌드는 *.abi3t.so보다 “자신의” *.abi3.so를 우선합니다.
다시 말해, (CPython의 x86_64-linux-gnu 빌드에서) importlib.machinery.EXTENSION_SUFFIXES는 다음과 같습니다:
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 태그가 붙은 wheel을 허용하는 모든 경우에, 자유 스레드 빌드에 대해서도 abi3t 태그가 붙은 wheel을 허용해야 합니다.
이 PEP는 자유 스레드 Python 3.14(cp314-abi3t) 이하용 안정 ABI를 대상으로 하는 방법을 제공하지 않는다는 점에 유의하십시오. 이는 향후, 또는 실험적인 빌드 도구에서 바뀔 수 있으므로, 설치 프로그램은 이러한 확장에 대비해야 합니다.
빌드 도구를 위한 권고 사항
빌드 도구는 사용자에게 새로운 선택지를 제공해야 합니다. 기존의 CPython 버전별 ABI(cp3nn)와 안정 ABI(abi3)용 컴파일에 더해, 다음 중 한 가지 방법으로 abi3와 abi3t 모두를 대상으로 확장을 한 번에 컴파일할 수 있도록 해야 합니다:
Py_LIMITED_API=v와Py_TARGET_ABI3T=v를 모두 정의하는 방법, 또는Py_LIMITED_API=v를 정의하고 다음과 같이 하는 방법:Py_GIL_DISABLED정의 (Windows에서)- 자유 스레드 CPython 헤더로 빌드 (그 외 플랫폼에서)
위에서 v는 확장이 호환되어야 하는 가장 낮은 Python 버전을 Py_PACK_VERSION() 형식으로 나타냅니다. 이 버전은 3.15 이상이어야 합니다.
PEP 3149에서 도입된 ABI 버전 태그가 붙은 .so 파일을 사용하는 시스템(Linux, macOS 및 이와 유사한 시스템)에서는 확장의 이름을 modulename.abi3t.so로 지정해야 합니다. 그 밖의 경우에는 달라지는 것이 없어야 합니다. Windows에서는 name.pyd를 사용하십시오.
이러한 확장을 포함하는 휠에는 압축된 ABI 태그 집합인 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 태그를 붙이는 방식입니다). 이렇게 하면 결과물은 자유 스레드 인터프리터에서만 사용할 수 있습니다.
CPython 3.14(또는 그보다 더 낮은 버전)와 호환되는 abi3t 확장을 빌드하는 것도 가능하지만, 이는 지원되지 않으며 제약 사항과 보장 사항에 대한 자세한 이해와 함께 철저한 테스트가 필요합니다.
새 API
이 PEP를 구현하면 자유 스레드 Python에서 성공적으로 로드될 수 있는 확장을 빌드할 수 있게 되지만, 그 확장이 반드시 GIL 없이도 스레드 안전한 것은 아닙니다.
GIL 없이 스레드 안전성을 확보할 수 있게 하는 제한된 API는 C API 작업 그룹을 통해 또는 후속 PEP에서 추가될 것입니다. (참고: PyCriticalSection API는 C API WG issue 100에 따라 3.15에 추가되었습니다.)
하위 호환성과 상위 호환성
abi3t를 대상으로 하는 확장은 PyModuleDef를 피하고 PEP 793에서 추가된 새 PyModExport 훅을 사용해야 하므로, 이전 CPython 릴리스와 소스 수준(API)에서도 컴파일된 형태(ABI)에서도 하위 호환되지 않습니다.
전환할 수 없는 확장 작성자는 기존 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 열의 값:
- existing: 해당 휠 태그가 현재 사용되고 있습니다.
- continued: 해당 휠 태그는 기존 체계를 이어 갑니다.
- new: 이 PEP에서 제안합니다.
- reserved: 이에 맞는 확장을 빌드하는 메커니즘은 이 PEP에서 제안하지 않지만, 향후 추가될 수 있습니다. 설치 프로그램은 이 태그를 처리할 수 있도록 대비해야 합니다.
보안 영향
알려진 바 없습니다.
교육 방법
포팅 가이드에서는 PEP 697 (불투명 타입 확장을 위한 제한된 C API)과 PEP 793 (PyModExport)에서 추가된 API로 옮겨 가는 방법을 설명해야 합니다.
참조 구현
이 PEP는 개별적으로 구현된 여러 요소를 결합합니다.
_Py_OPAQUE_PYOBJECT매크로를 정의하면 CPython main 브랜치에서 불투명한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에서 설치 프로그램의 wheel 태그 처리를 구현했습니다.- 빌드 도구에 대해서는 여러 개별 풀 리퀘스트가 만들어졌습니다. 자세한 내용은 Nathan에게 문의하십시오.
이 PEP가 수락된 후 구현은 CPython issue 146636에서 추적되었습니다.
기각된 아이디어
단일 신규 ABI: 안정 ABI 3.15에서 PyObject를 불투명하게 만들기
안정 ABI의 새로운 변형을 도입하는 대신 안정 ABI에서 PyObject 구조체를 불투명하게 만들 수도 있습니다.
이렇게 하면 확장 작성자는 Python 3.15에서 도입된 C API를 사용하기 위해 코드를 새로운 제약에 맞게 수정하거나, 안정 ABI를 아예 포기해야 합니다.
또한 이 방법으로도 새 휠 태그(abi3t)의 필요성이 완전히 사라지지는 않습니다. 이 태그는 확장이 CPython 3.14 이하의 GIL 활성화 빌드와 자유 스레드 빌드 모두와 호환된다는 것을 표현하는 데 필요합니다.
PEP 논의에서는 소스 변경 없이 GIL 전용 안정 ABI용으로 빌드할 수 있는 기능이 추가 설정 매크로(현재 Py_TARGET_ABI3T라고 부름)를 둘 만한 가치가 있다고 판단되었습니다.
CPython 3.14와의 호환성을 위한 심(shim)
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 기반 설치 프로그램이 wheel 태그 매칭을 구현하는 데 일반적으로 사용하는 라이브러리입니다.)
설치 프로그램 외에도, abi3.abi3t 태그를 사용하면 PyPI 파일 목록(예: https://pypi.org/project/cryptography/#files)의 ABI 필터 같은 메커니즘이 특수 처리 없이 abi3과 일치시킬 수 있습니다(압축된 태그가 표준에 따라 처리된다고 가정할 때). 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이 활성화된 인터프리터에서 이러한 확장 모듈은 필수 런타임 ABI 검사에 실패하거나, abi3t와 abi3가 갈라지는 가능성 낮은 미래에는 링커 심볼 누락으로 인해 로드에 실패할 수도 있습니다.
개념적으로, 파일 이름 태그는 확장 모듈의 ABI를 “기술”하거나 “명명”하지 않습니다. 현재의 .abi3 태그는 버전 정보가 없으므로 이미 이런 용도로 쓰기에는 너무 약합니다.
파일 이름 태그 없음(맨 .so)
파일 이름 ABI 태그를 아예 없애고 .abi3t.so 대신 .so를 사용하는 것도 가능합니다. 두 태그의 실제 의미는 매우 비슷합니다. .so는 CPython의 어떤 빌드에서도 로드할 수 있고, .abi3t.so는 3.15 이상의 모든 CPython에서 로드할 수 있습니다. 하지만 안정 ABI 파일 이름 태그에는 이미 버전 정보가 없습니다.
하지만 의미상으로는 서로 다릅니다. 맨 .so는 “상관없음”을 의미하고, .abi3t.so 확장 모듈은 의도적으로 새 ABI와 호환됩니다.
새 ABI를 활성화하는 데 Py_GIL_DISABLED 재사용하기
Py_GIL_DISABLED 매크로가 Py_LIMITED_API와 함께 정의되었을 때 (abi3 대신) abi3t를 선택하는 것도 가능합니다.
이렇게 하면 빌드 플래그를 번거롭게 조정해야 하며, abi3와 abi3t를 동시에 명시적으로 대상으로 삼을 수 없게 됩니다.
소스 호환성을 유지하는 완전히 분리된 ABI
abi3와 abi3t를 완전히 분리하여 서로 호환되지 않게 만드는 것도 가능합니다. 이렇게 하면 현재의 모든 확장이 abi3t와 소스 수준의 호환성을 유지할 수 있습니다. 버전별 CPython ABI에서처럼 각 ABI가 서로 다른 비공개 필드 집합을 정의하면서 PyObject 구조체를 계속 노출할 수 있기 때문입니다.
그러나 노출된 PyObject 구조체는 기존 안정 ABI의 주요 단점 중 하나로 지적되었습니다. 이는 불멸화(immortalization)와 자유 스레딩 자체 같은 최적화와 기능을 방해하거나 불가능하게 만들었습니다.
PyObject를 노출하는 것은 이 실수를 반복하여 현재의 자유 스레드 정의를 “동결”하고, 변경이 필요할 때 안정 ABI의 또 다른 변형을 요구하게 된다는 의미입니다.
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.