Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 809 – 미래를 위한 Stable ABI

Author:
Steve Dower <steve.dower at python.org>
Discussions-To:
Discourse thread
Status:
Draft
Type:
Standards Track
Requires:
703, 793, 697
Created:
19-Sep-2025
Python-Version:
3.15
Post-History:
30-Sep-2025

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

abi3로서의 Stable ABI는 더 이상 유지할 수 없으므로 대체가 필요합니다. abi2026은 첫 번째 대체 ABI가 되며, 현재 알려진 비호환성을 해결하고 최소 10년 후 폐기할 계획입니다. 다음 ABI(예: abi2031)는 이전 ABI와 최소 5년 동안 중복되어 제공됩니다.

런타임 ABI 검색 메커니즘을 통해 장기적인 안정성을 확보하며, 동일한 ABI를 지원하는 이전 릴리스에서 확장 기능을 실행할 수 있습니다. ABI의 수명 동안 변경 사항과 추가 사항은 인터페이스로 추가할 수 있으며, 런타임에 이를 검색하여 호출자가 적절한 대체 동작을 선택할 수 있습니다. 현재는 이러한 추가 사항으로 인해 이전 런타임에서 확장 기능을 전혀 로드할 수 없습니다.

abi3 ABI는 GIL 활성화 빌드에서 최소 5년 동안 유지되며, 그 후 폐기될 수 있습니다(abi2026 및 이후 버전만 사용할 수 있게 됩니다). 그 전에 GIL 활성화 빌드가 완전히 폐기될 가능성도 있습니다. 프리 스레드 빌드에는 abi3이 없으므로, 해당 빌드의 첫 Stable ABI는 abi2026이 됩니다.

용어

이 PEP에서는 “GIL 활성화 빌드”를 “프리 스레드 빌드”의 반의어로 사용합니다. 즉, Py_GIL_DISABLED 없이 빌드된 인터프리터 또는 확장 기능을 의미합니다.

동기

현재 Stable ABI는 프리 스레드 빌드에서 사용할 수 없습니다. 확장 기능은 Py_LIMITED_API가 정의된 경우 빌드에 실패합니다. 마찬가지로 CPython의 GIL 활성화 빌드용으로 빌드된 확장 기능은 프리 스레드 빌드에서 로드에 실패하거나 충돌합니다.

Steering Council은 PEP 779acceptance post에서 자유 스레딩을 위한 Stable ABI가 Python 3.15를 위해 준비되고 정의되어야 한다고 “기대합니다”라고 밝혔습니다.

이 PEP는 3.15 및 이후 버전의 모든 변형과 호환되는 Stable ABI를 제안하며, 패키지 개발자가 확장 기능을 한 번만 빌드할 수 있도록 합니다.

관련 PEP

PEP 803은 이 제안의 대안이며, 이 PEP에서 사용한 배경 설명의 상당 부분은 의도적으로 동일합니다. PEP 803 작성자에게 해당 텍스트를 사용할 수 있도록 해 주신 데 감사드립니다.

배경

Python의 Stable ABI는 PEP 384PEP 652에 정의된 대로, 여러 마이너 버전의 CPython 인터프리터에서 로드할 수 있는 확장 모듈을 컴파일하는 방법을 제공합니다. 여러 프로젝트에서는 이를 사용하여 각 릴리스에 대해 빌드하고 배포해야 하는 (바이너리 아티팩트)의 수를 줄이거나, Python의 사전 릴리스 버전으로 테스트하기 쉽게 만들고 있습니다.

프리 스레딩 빌드(PEP 703)가 결국 기본값이 될 예정(PEP 779)이므로, 이러한 빌드에서 Stable ABI를 사용할 수 있게 하는 방법이 필요합니다.

Stable ABI를 대상으로 빌드하려면 확장 기능이 Limited API를 사용해야 합니다. 즉, CPython이 노출하는 함수, 구조체 등의 일부만 사용해야 합니다. Limited API에는 버전이 지정되며, Limited API 3.X를 대상으로 빌드하면 CPython 3.X 및 이후의 모든 버전과 ABI 호환되는 확장 기능이 생성됩니다(다만 CPython의 버그로 인해 실제로는 비호환성이 발생하는 경우가 있습니다). 또한 Limited API는 “안정적”이지 않습니다. 최신 버전에서는 이전 버전에서 사용할 수 있었던 API 항목을 제거할 수 있습니다.

이 PEP는 Limited API와 Stable ABI의 버전 관리 방식 모두에 중대한 변경을 제안합니다. 안정성과 호환성을 장기적으로 관리하는 동시에 제한된 부분집합의 사용자가 이후 Python 릴리스의 혁신에 접근할 수 있도록 하는 것이 목표입니다.

근거

이 PEP의 설계는 몇 가지 가정을 전제로 합니다:

단일 ABI
단일 컴파일 확장 모듈이 자유 스레드 빌드와 GIL 활성화 빌드를 모두 지원해야 합니다.
하위 호환성 없음
새로운 제한 API는 CPython 3.14 이하에서 지원되지 않습니다. 이 지원이 필요한 프로젝트는 3.14 자유 스레드 인터프리터용 확장과 이전 안정 ABI 버전용 확장을 별도로 빌드할 수 있습니다.
API 변경은 허용됩니다.
새로운 제한 API를 사용하려면 확장 모듈 작성자가 코드에 상당한 변경을 가해야 할 수 있습니다. 아직 그렇게 할 수 없는 프로젝트는 제한 API 3.14를 계속 사용할 수 있으며, 이 경우 GIL 활성화 빌드와만 호환되는 확장이 생성됩니다.
추가 구성 없음
어떤 API를 사용할 수 있는지와 어떤 ABI와 호환되는지에 영향을 미치는 새로운 “노브”를 도입하지 않습니다.

사양

사양의 상당 부분이 PEP 803과 동일하므로, 자세한 내용은 독자가 해당 제안을 참조해야 한다는 점에 유의하십시오. ABI 안정성, 빌드 시 매크로 및 인터페이스 API 섹션은 이 제안에만 해당합니다.

ABI 안정성

안정 ABI는 최소 10년 동안 고정됩니다. 안정 ABI의 새 버전이 고정되면 기존 버전은 최소 5년 동안 계속 지원됩니다. 이를 통해 패키지 유지 관리자는 물론 다른 사용자도 지원하는 릴리스의 전체 범위를 동시에 마이그레이션할 충분한 시간을 확보할 수 있습니다. 그러나 Python 핵심 개발 팀이 현재 안정 ABI를 교체할 이유가 없다고 판단하면 새 버전의 고정을 미룰 수 있습니다.

새로운 안정 ABI는 PEP 프로세스를 사용하여 정의하며, 이름에는 이를 지원하는 최초 런타임의 릴리스 연도가 반영됩니다.

안정 ABI가 고정되면 해당 연도가 ABI의 이름이 됩니다. 예를 들어 이 방식에 따른 첫 ABI는 abi2026이 될 것으로 예상하며, 최소 2036년까지 모든 릴리스에서 지원됩니다. 2036년에 지원이 중단된다면 abi2031이 마이그레이션 대상이 되며, 이를 통해 패키지 개발자는 자체 마이그레이션 후 최소 5년간의 릴리스를 지원할 수 있습니다.

고정된 동안에는 ABI 변경이 전혀 허용되지 않습니다. 추가, 제거, 수정 또는 급격한 의미 변경도 허용되지 않습니다. 특히 특정 ABI에 대해 컴파일된 확장 모듈은 해당 ABI를 지원하는 모든 Python 버전에서, 이전 버전이든 이후 버전이든, 성공적으로 로드되어야 합니다(즉, 지원되는 모든 플랫폼에서 가져온 기호가 충족되어야 합니다).

기존의 호환 가능한 ABI를 통해 런타임에 감지할 수 없는 의미 변경은 허용되지 않습니다. 즉, 특정 동작이 현재 Python 릴리스에서 예상되는 동작인지 감지하는 API가 해당 ABI를 지원하는 모든 이전 릴리스에서 사용 가능해야 합니다.

불투명 PyObject

제한 API 버전 3.15에서는 여러 구조체를 불투명하게 만들어, 해당 구조체의 사용자가 크기나 레이아웃에 대해 어떠한 가정도 할 수 없게 합니다. 자세한 내용은 PEP 803에서 확인할 수 있으며, 이 제안도 동일합니다.

새로운 내보내기 후크 (PEP 793)

이 PEP를 구현하려면 PEP 793 (PyModExport: C 확장 모듈을 위한 새로운 진입점)를 승인하여 확장 모듈을 정의하기 위한 새로운 “내보내기 후크”를 제공해야 합니다. 새로운 후크의 사용은 Limited API 3.15에서 의무화됩니다.

이 제안은 PEP 803의 제안과 동일합니다.

런타임 ABI 검사

자세한 내용은 PEP 803을 참조하십시오. 이 제안은 동일합니다.

빌드 시 매크로

관련 Py_LIMITED_API0x03ff_YYYY로 정의되도록 요구합니다. 즉, 상위 워드는 상수 0x03ff이고 하위 워드는 ABI 이름(연도)을 16진수 값으로 나타낸 것입니다. 이로 인해 연도와 동일하지 않은 십진수 값이 되지만, 이 값은 임의의 레이블이며 계산된 값보다는 상수(cc 명령줄에서 지정되는 형태)로 지정될 가능성이 높으므로 중요하지 않다고 판단합니다.

0x03ff를 상수로 사용하는 것은 이전 런타임과의 호환성을 허용하기 위한 것입니다. abi3만 지원하는 헤더에서 동일한 상수를 사용하면 해당 릴리스에서 사용할 수 있는 ABI3의 “가장 완전한” 버전이 선택됩니다. 예를 들어 3.15 이상에서 0x03ff2026을 사용하면 abi2026이 선택되지만, 3.10에서는 3.10~3.14에서 작동하는 ABI3 버전이 선택됩니다.

휠 태그

휠에는 ABI 태그 abi2026을 지정해야 합니다. Python 태그나 플랫폼 태그를 변경할 필요는 없습니다. cp314 또는 이전 버전으로 태그된 릴리스는 abi2026와 절대 호환되지 않는다는 점에 주목할 필요가 있습니다. 해당 ABI 태그가 당시에는 존재하지 않았기 때문이며, 따라서 py3-abi2026-<plat>로 태그된 휠이 새 Stable ABI를 사용하는 휠을 이전 릴리스에서 로드하게 만들지는 않습니다.

새 API

이 PEP를 구현하면 프리 스레드 Python에서 성공적으로 로드할 수 있는 확장 모듈을 빌드할 수 있지만, GIL 없이 스레드 안전한 확장 모듈까지 반드시 빌드할 수 있는 것은 아닙니다.

GIL 없이 스레드 안전성을 허용하는 Limited API(아마도 PyMutex, PyCriticalSection 및 유사한 기능)는 C API 작업 그룹을 통해 또는 후속 PEP에서 추가될 예정입니다.

인터페이스 API

Python과 새로운 Limited API에 새로운 interfaces API가 추가됩니다. 이 API는 위의 ABI 안정성 절에 있는 “의미적 변경을 모든 릴리스에서 감지할 수 있어야 한다”는 요구 사항을 충족하기 위한 것입니다. 즉, 소비자 [1]는 새 API를 즉시 채택하고, 최신 릴리스의 Limited API용으로 컴파일하며, 해당 ABI를 지원하는 모든 릴리스에서 바이너리 호환성을 유지할 수 있습니다.

요약하면 기본 API는 PyObject_GetInterface()이며, 데이터 또는 함수 포인터를 포함하는 C 구조체를 채우기 위해 새로운 네이티브 전용 타입 슬롯에 위임합니다. C 구조체 정의는 런타임에 가져오는 것이 아니라 확장 모듈에 내장되므로, 확장 모듈은 해당 구조체를 제공하지 않는 Python 릴리스에서 실행되는 동안에도 이후의 구조체를 인식할 수 있습니다.

PyObject_GetInterface호출이 현재 버전에서 사용할 수 없는 구조체를 요청하거나 제공된 객체에 사용할 수 없는 구조체를 요청하면 호출은 안전하게 실패합니다. 그러면 호출자는 선호도에 따라 대체 로직(예를 들어 추상 Python API 사용)을 사용하거나 중단할 수 있습니다.

예를 들어 abi2026의 수명 동안 int객체의 내부 데이터에 더 효율적으로 접근할 수 있는 새 API를 추가해야 한다면, 새 API를 추가하는 대신 데이터를 새 위치로 복사하는 함수 포인터를 포함하는 구조체와 해당 인터페이스에 대해 이전에 사용되지 않은 인덱스/이름으로 구성된 새 인터페이스를 만들 것입니다. 호출자는 먼저 PyObject_GetInterface(int_object, &intf_struct)를 호출할 수 있습니다. 성공하면 (가상의) (*intf_struct.copy_bits)(&intf_struct, dest, sizeof(dest))를 호출하고, 실패하면 PyObject_CallMethod(int_object, "to_bytes", ...)를 사용하여 동일한 작업을 수행할 수 있지만 효율은 더 낮습니다. 이 예의 최종 결과는 abi2026을 지원하는 all 릴리스와 바이너리 호환되면서도 최신 Python 릴리스에서 실행할 때 더 효율적인 단일 확장 모듈입니다.

개요는 여기까지이며, 다음은 각각의 새 API에 대한 전체 사양입니다:

// Abstract API to request an interface for an object (or type).
PyAPI_FUNC(int) PyObject_GetInterface(PyObject *obj, void *intf);

// API to release an interface.
PyAPI_FUNC(int) PyInterface_Release(void *intf);

// Expected layout of the start of each interface. Actual interface structs
// will add additional function pointers or data.
typedef struct PyInterface_Base {
    // sizeof(self), for additional validation that the caller is passing
    // the correct structure.
    Py_ssize_t size;

    // Unique identifier for the struct. Details below.
    uint64_t name;

    // Function to release the struct (e.g. to decref any PyObject fields).
    // Should only be invoked by PyInterface_Release(), not directly.
    int (*release)(struct PyInterface_Base *intf);
} PyInterface_Base;

// Type slot definition for PyTypeObject field.
typedef int (*Py_getinterfacefunc)(PyObject *o, PyInterface_Base *intf);

구조체의 고유 식별자는 매크로로 정의된 64비트 정수입니다(컴파일된 확장 모듈에 런타임에 값을 알아내려고 시도하는 대신 값이 포함되도록 하기 위한 것입니다). 상위 32비트는 네임스페이스이며, 자체 구조체를 정의하는 구현자는 자신을 위해 고유한 값을 선택해야 합니다. 0은 CPython을 위해 예약되어 있습니다.

인터페이스 이름은 구조체 레이아웃을 식별하기 위한 것이므로, 구조체가 일치한다면 정의된 모든 객체가 다른 네임스페이스의 인터페이스 이름을 재사용할 수 있습니다. 이는 의도된 동작이며, 구현을 공유하는 데 의존하지 않고도 서드파티 타입이 핵심 타입과 동일한 인터페이스를 구현할 수 있도록 합니다. 명확히 말하면, CPython용으로 정의된 인터페이스는 이름이나 해당 이름의 네임스페이스를 변경하지 않고 다른 확장 모듈에서 사용할 수 있습니다.

예를 들어 PyDict_GetItemString()을 구현하기 위한 가상의 인터페이스를 생각해 보십시오. 핵심 dict 타입은 문자열 키로 항목을 찾기 위한 내부 최적화를 수행할 수 있는 반면, 외부 타입은 동일한 인터페이스를 사용하여 자체 최적화를 수행할 수 있습니다. 호출자에게는 동일한 인터페이스를 사용하는 것처럼 보이므로, 예를 들어 CPython의 구체적인 객체 API를 사용하는 경우보다 더 광범위한 타입과 호환됩니다.

인터페이스 이름은 어떤 시점에도 헤더에서 제거할 수 없으며, 구조체 정의는 이를 지원하는 모든 Stable ABI 버전이 완전히 폐기된 경우에만 제거할 수 있습니다. 그러나 이전 릴리스에서 특정 인터페이스를 반환했더라도, 더 이상 권장되지 않거나 신뢰할 수 없다면 객체가 해당 인터페이스의 반환을 중단할 수 있습니다. 적절한 경우 런타임 지원 중단 경고를 사용할 수 있으며, 특정 규칙은 지정되어 있지 않습니다.

인터페이스 구조체는 고정되어 있으며 변경할 수 없습니다. 변경이 필요한 경우 새 이름으로 새 인터페이스를 정의해야 합니다. 인터페이스를 위해 구조체에 추가된 필드는 공개 API이므로 문서화해야 합니다. 직접 사용하도록 의도되지 않은 필드는 밑줄로 시작해야 하지만, 그 외에는 “비공개”로 만들 수 없습니다. 인터페이스는 데이터 포인터와 함수 포인터를 혼합하여 제공하거나, 경쟁 조건을 방지하기 위해 강한 PyObject * 참조를 사용할 수 있습니다.

인터페이스를 가져온 후에는 객체에 대한 참조가 해제되더라도 인터페이스가 해제될 때까지 유효한 상태로 유지되어야 합니다. 인터페이스의 동작은 기본 객체의 변경을 적절한 방식으로 처리할 수 있지만, 그 선택 사항을 문서화해야 할 가능성이 높습니다. 이러한 종류의 변경을 서로 다르게 처리하는 유사한 인터페이스를 두 개 두는 것도 무리가 아닙니다(예: 하나의 인터페이스는 인터페이스의 수명 동안 객체를 잠그고 다른 인터페이스는 잠그지 않는 경우).

새 Limited API를 추가하는 과정은 다소 변경됩니다. 각 릴리스마다 증가하는 ABI를 두는 대신, Limited API를 사용하지 않을 때는 새 API를 실제 함수로 추가할 수 있지만 Limited API에는 정적 인라인 함수로 추가해야 합니다. 이 정적 인라인 함수는 런타임에 기능을 감지하기 위해 인터페이스를 사용해야 하며, 추상 폴백 또는 적절한 예외를 포함해야 합니다.

이는 사용자가 새 API를 즉시 채택하고, 최신 릴리스의 Limited API용으로 컴파일하며, 동일한 Stable ABI를 지원하는 모든 릴리스에 대해 바이너리 호환성을 유지할 수 있음을 의미합니다.

다음 Stable ABI 동결 시 API를 새 Stable ABI/Limited API의 실제 함수로 승격하거나 인터페이스로 유지할 수 있습니다.

하위 호환성

제거된 구조체와 함수로 인해 Limited API 3.15는 이전 CPython 릴리스와 하위 호환되지 않습니다.

전환할 수 없는 확장 작성자는 GIL 활성화 빌드에서 사용하기 위해 Limited API 3.14 이하를 계속 사용할 수 있습니다.

GIL 활성화 빌드의 abi3에는 변경이 이루어지지 않으며, 새 Stable ABI에서는 더 이상 사용할 수 없더라도 기존의 모든 기호를 계속 사용할 수 있습니다.

CPython의 자유 스레드 빌드를 기본 또는 유일한 릴리스로 만드는 것은 하위 호환성이 깨지는 변경이며, 확장 작성자는 마이그레이션을 완료해야 합니다.

보안 관련 사항

알려진 사항이 없습니다.

이 내용을 가르치는 방법

Python의 네이티브 ABI는 다른 언어와 마찬가지로 연도별로 식별되는 주기적으로 업데이트되는 표준 또는 사양으로 설명할 수 있습니다. 모든 확장 모듈은 이 ABI를 사용할 수 있으며, 배포 정보의 일부로 예상하는 ABI를 선언합니다. 모든 Python 구현은 특정 ABI 버전을 지원하도록 선택할 수 있으며, 해당 버전을 지원하는 모든 확장 기능을 사용할 수 있어야 합니다.

abi3에서 새로운 ABI로 마이그레이션하려면 소스 코드 변경이 필요할 수 있지만, 일회성 작업으로 처리할 수 있습니다. 많은 경우, 대부분의 경우라고 해도 소스 코드는 abi3와 새로운 ABI 모두와 호환되므로 이전 릴리스와 현재 릴리스용 빌드 생성을 간소화할 수 있습니다. 일반적으로 abi3 빌드는 지원되는 가장 오래된 CPython 런타임으로 빌드해야 하며, 새로운 ABI 빌드는 최신 CPython 런타임 또는 다른 호환 가능한 런타임으로 빌드해야 합니다.

한 ABI(예: abi2026)에서 다음 ABI(예: abi2031)로 마이그레이션하는 작업은 수동으로 수행해야 합니다. ABI 업데이트 간에는 충분한 중복성이 있으므로 대부분의 프로젝트는 한 번에 하나만 지원하면 되며, 자체 지원 매트릭스에서 허용하는 경우 모든 빌드를 한 번에 업데이트할 수 있습니다. 패키지 유지 관리자가 각각의 새로운 ABI를 즉시 지원해야 한다는 기대는 없습니다.

순방향 및 하위 호환성은 동적 인터페이스 검색으로 보장됩니다. 최근 추가된 Limited API 함수를 사용하는 코드는 성능이 낮아질 가능성은 있지만 이전 릴리스에서도 실행됩니다. 새로운 함수의 문서에서 Limited API 관련 특이 사항을 확인하십시오.

C가 아닌 호출자는 최신 릴리스로의 호환성을 인위적으로 제한하지 않고 새로운 기능에 접근할 수 있도록 인터페이스 메커니즘을 직접 사용해야 합니다. 인터페이스의 이름과 구조체 레이아웃은 영구적으로 안정된 상태가 보장되지만, 인터페이스를 항상 사용할 수 있다고 가정해서는 안 되며 적절한 대체 코드(대체 구현 또는 오류 처리)를 포함해야 합니다.

참조 구현

해당 PEP에서 상속된 측면에 대한 참조 구현 링크는 PEP 803을 참조하십시오.

인터페이스의 참조 구현은 zooba/cpython#44입니다.

거부된 아이디어

[현재는 논의를 참조하십시오.]

미해결 문제

[현재는 논의를 참조하십시오.]

각주