PEP 652 – 안정 ABI 유지 관리
- Author:
- Petr Viktorin <encukou at gmail.com>
- Discussions-To:
- Discourse thread
- Status:
- Final
- Type:
- Standards Track
- Created:
- 09-Feb-2021
- Python-Version:
- 3.10
- Resolution:
- Python-Dev message
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
CPython의 제한된 C-API 및 안정 ABI는 PEP 384에서 도입되었으며, 단일 definitive file에서 공식화되고 테스트되며 문서화됩니다.
동기
PEP 384는 CPython의 확장 기능 개발자와 임베더가 3.x의 이후 모든 버전과 바이너리 호환되는 확장 모듈을 컴파일할 수 있도록 하는 제한 API와 안정 ABI를 정의했습니다. 이론적으로 이는 다음과 같은 여러 이점을 제공합니다.
- 모듈을 플랫폼별로 한 번만 빌드하고 여러 Python 버전을 지원할 수 있으므로, 빌드에 필요한 시간과 전력 및 유지 관리자 인력을 줄일 수 있습니다(성능이 저하될 가능성을 감수하는 대신).
- 안정 ABI를 사용하는 바이너리 휠은 사전 릴리스 기간 내내 새로운 CPython 버전에서 작동하며, 소스에서 빌드하는 것이 실용적이지 않은 환경에서도 테스트할 수 있습니다.
- 제한 API가 구현 세부 사항을 숨김으로써 얻는 반가운 부수 효과로, 이 API는 전체 C API와 호환되지 않는 대체 Python 구현의 실행 가능한 대상으로 자리 잡고 있습니다.
그러나 돌이켜보면 PEP 384와 그 구현에는 몇 가지 문제가 있습니다.
- 정의가 불분명합니다. 관련 PEP 384에 따르면, 함수는 옵트아웃 방식입니다. 즉, 특별히 표시되지 않은 모든 함수는 안정 ABI의 일부입니다. 실제로 Windows에는 opt-in인 목록이 있습니다. 사용자에게는 안정 ABI만 사용할 수 있게 해야 하는
#define이 있지만, 이를 최신 상태로 유지하도록 보장하는 프로세스가 없습니다. 문서를 업데이트하는 프로세스도 없습니다. - 최근까지 안정 ABI는 전혀 테스트되지 않았습니다. 안정 ABI는 깨지기 쉽습니다. 예를 들어 함수를 매크로로 변경하면 함수 심볼이 제거되므로 안정 ABI가 깨질 수 있습니다.
- 제한 API의 일부를 사용 중단할 방법이 없습니다.
- 불완전합니다. 안정 ABI에서 사용할 수 없는 연산이 일부 있으며, 그 이유는 거의 대부분 “잊어버렸기 때문”입니다. (그러나 이 마지막 문제에는 이 PEP가 도움이 되지 않습니다.)
이 PEP는 제한 API를 더 명확하게 정의하고, 안정 ABI와 제한 API를 더욱 유용하고 견고하게 만들도록 설계된 프로세스를 도입합니다.
근거
이 PEP에는 많은 명확화와 정의가 포함되어 있지만, 큰 기술적 변경은 하나뿐입니다. 안정 ABI를 사람이 유지 관리하는 “매니페스트” 파일에 명시적으로 나열합니다.
예를 들어 Python에서 내보낸 심볼을 검사하여 이러한 목록을 자동으로 수집하려는 시도가 있었습니다. 이러한 자동화는 직접 작성한 파일보다 유지 관리하기 쉬워 보일 수 있지만, 중대한 문제가 있습니다. 예를 들어 내보낸 심볼 집합은 플랫폼별로 달라집니다. 또한 명시적인 매니페스트를 업데이트하는 비용은 영구적으로 지원해야 하는 API를 변경하는 데 들여야 할 전체 작업(또는 그보다 먼저 Python 3의 수명이 끝나는 경우에는 그 시점까지의 작업)에 비하면 적습니다.
이 PEP는 매니페스트 로부터 항목을 자동으로 생성할 것을 제안합니다. 처음에는 문서와 DLL 콘텐츠를 생성하고, 이후에는 테스트도 자동화할 수 있습니다.
안정 ABI와 제한 API
PEP 384와 이 문서는 서로 관련되어 있지만 구별되는 두 개념인 제한 API와 안정 ABI를 다룹니다. 간단히 말하면:
- Stable ABI”은 CPython 3.x로 컴파일된 특정 확장 기능이 이후의 모든 CPython 3.x 버전과 바이너리 호환된다는 약속입니다.
- Limited API”는 이러한 확장 기능을 생성하는 CPython C API의 하위 집합입니다.
이 절에서는 이러한 용어를 명확히 설명하고 그 의미 체계 중 일부(기존에 존재했거나 여기에서 새로 제안된 것)를 정의합니다.
“Extensions”라는 단어는 Python API를 사용하는 모든 코드(예: 확장 모듈 또는 Python을 임베드하는 소프트웨어)를 간단히 가리키는 말로 사용됩니다.
Stable ABI
CPython Stable ABI”는 특정 Stable ABI 버전을 대상으로 빌드된 확장 기능이 동일한 주 버전의 모든 최신 인터프리터와 호환된다는 약속입니다.
Stable ABI는 완전한 바이너리 인터페이스를 정의하지 않습니다. 메모리 내 구조체의 배치나 함수 호출 규약과 같은 중요한 세부 사항은 플랫폼과 컴파일러 및 그 설정에 따라 결정됩니다. Stable ABI에 대한 약속은 이러한 하위 수준 세부 사항도 안정적인 경우에만 적용됩니다.
예를 들어 CPython 3.10 Stable ABI로 빌드된 확장 기능은 CPython 3.11, 3.12 등에서 사용할 수 있습니다. 그러나 CPython 4.0과 반드시 호환되는 것은 아니며, 다른 플랫폼의 CPython 3.10과도 반드시 호환되는 것은 아닙니다.
Stable ABI는 일반적으로 상위 호환되지 않습니다. CPython 3.10으로 빌드하고 테스트한 확장 기능은 일반적으로 CPython 3.9와 호환되지 않습니다.
Note
예를 들어 Python 3.10부터는 Py_tp_doc 슬롯을 NULL로 설정할 수 있지만, 이전 버전에서는 NULL 값으로 인해 인터프리터가 충돌할 가능성이 높습니다.
Stable ABI는 안정성을 위해 성능을 절충합니다. 예를 들어 특정 CPython 버전용으로 빌드된 확장 기능은 Stable ABI의 함수 대신 더 빠른 매크로를 자동으로 사용합니다.
향후 Python 버전에서는 Stable ABI의 일부 멤버가 더 이상 사용되지 않게 될 수 있습니다. 더 이상 사용되지 않게 된 멤버도 계속 작동하지만, 성능 저하나 가장 심각한 경우의 메모리/리소스 누수와 같은 문제를 겪을 수 있습니다.
Limited API
Stable ABI에 대한 약속은 Limited API”(애플리케이션 프로그래밍 인터페이스)로 제한된 코드에서 컴파일된 확장 기능에 적용됩니다. Limited API는 CPython C API의 하위 집합입니다.
Limited API를 대상으로 하는 확장 기능은 전처리기 매크로 Py_LIMITED_API를 3 또는 현재의 PYTHON_API_VERSION으로 정의해야 합니다. 이렇게 하면 여러 함수의 Stable ABI 버전이 활성화되고 정의가 Limited API로 제한됩니다. (단, 이 매크로가 완벽하지는 않다는 점에 유의하십시오. 기술적 문제나 누락으로 인해 매크로를 정의해도 일부 비제한 API가 노출될 수 있습니다.)
Limited API가 안정적이라고 보장되지는 않습니다. 향후 Limited API의 일부가 더 이상 사용되지 않게 될 수 있습니다. Stable ABI”가 안정적으로 유지되고 Python의 일반적인 하위 호환성 정책인 PEP 387을 따르는 한, 해당 요소가 제거될 수도 있습니다.
Note
예를 들어 함수 선언이 공개 헤더 파일에서는 제거되지만 라이브러리에는 유지될 수 있습니다. 이는 현재로서는 향후 발생할 수 있는 가능성입니다. 이 PEP에서는 더 이상 사용되지 않게 하거나 제거하기 위한 구체적인 절차를 제안하지 않습니다.
Limited API의 목표는 인터프리터와 상호 작용하는 데 필요한 모든 것을 포함하는 것입니다. 공개 API를 Limited 하위 집합에 포함하지 않는 주된 이유는 CPython 버전 간에 변경되는 구현 세부 사항(예: 구조체 메모리 배치)이 필요하기 때문이어야 하며, 이는 일반적으로 성능상의 이유입니다.
Limited API는 CPython에만 제한되지 않습니다. 다른 구현체에서도 이를 구현하고 그 설계를 발전시키는 데 기여할 것을 권장합니다.
사양
Stable ABI를 더욱 유용하고 견고하게 만들기 위해 다음 변경 사항을 제안합니다.
Stable ABI 매니페스트
Stable ABI의 모든 구성원(함수, typedef, 구조체, 데이터, 매크로 및 상수)은 하나의 “manifest” 파일인 Misc/stable_abi.txt에 명시적으로 나열됩니다.
구조체의 경우 Stable ABI 사용자가 액세스할 수 있는 모든 필드가 명시적으로 나열됩니다.
또한 매니페스트는 Limited API의 권위 있는 목록으로 사용됩니다. Limited API의 일부는 아니지만 Stable ABI의 일부인 구성원(예: Py_TYPE매크로를 통해 액세스할 수 있는 PyObject.ob_type)에는 그러한 구성원임을 나타내는 주석이 추가됩니다.
일부 시스템에서만 사용할 수 있는 항목의 경우, 매니페스트에는 해당 항목의 존재 여부를 결정하는 기능 매크로(예: MS_WINDOWS 또는 HAVE_FORK)가 기록됩니다. 구현과 비C 언어에서의 사용을 쉽게 하기 위해 이러한 모든 매크로는 단순한 이름으로 지정됩니다. 향후 항목에 “부정” 매크로나 복잡한 표현식(가상의 #ifndef MACOSX 또는 #if defined(POSIX) && !defined(LINUX) 등)이 필요한 경우에는 새로운 기능 매크로가 파생됩니다.
매니페스트의 형식은 필요할 때마다 변경될 수 있습니다. 매니페스트는 CPython 저장소의 스크립트에서만 사용해야 합니다. 안정적인 목록이 필요한 경우 이를 생성하는 스크립트를 추가할 수 있습니다.
다음 항목은 ABI 매니페스트에서 생성됩니다.
- Windows 공유 라이브러리의 소스인
PC/python3dll.c입니다. - 문서용 입력(아래 참조)입니다.
- 기호의 런타임 가용성을 확인하는 테스트 케이스(아래 참조)입니다.
다음 항목은 지속적 통합의 일부로 Stable ABI 매니페스트와 대조하여 검사됩니다.
- 참조 카운트 요약인
Doc/data/refcounts.txt에는 Stable ABI의 모든 함수가 포함됩니다(그 밖의 함수도 포함됩니다). Python.h를Py_LIMITED_API가 설정된 상태로 포함할 때 선언되는 함수와 구조체, 그리고 정의되는 상수와 매크로입니다. (처음에는 Linux만 해당하며, 향후 다른 시스템에 대한 검사도 추가될 수 있습니다.)
초기 구현 이후 함수 인자와 같은 세부 사항이 추가되고, 매니페스트의 내부 일관성이 검사됩니다(예: 함수 시그니처에 사용되는 모든 타입이 API의 일부인지 여부).
Stable ABI의 내용
초기 Stable ABI 매니페스트에는 다음 항목이 포함됩니다.
- Stable ABI는 PEP 384에서 지정된 것입니다.
PC/python3dll.c에 나열된 모든 항목입니다.- 이러한 함수가 반환하거나 인자로 받는 모든 구조체(구조체 typedef)입니다. (이러한 구조체의 필드는 반드시 추가되지는 않습니다.)
Py_am_aiter와 같은 새로운 타입 슬롯입니다.- 타입 플래그
Py_TPFLAGS_DEFAULT,Py_TPFLAGS_BASETYPE,Py_TPFLAGS_HAVE_GC,Py_TPFLAGS_METHOD_DESCRIPTOR입니다. - 호출 규약
METH_*입니다(더 이상 사용되지 않는 것은 제외합니다). - 매크로에 필요한 모든 API는 Stable ABI에 속합니다(Limited API의 일부가 아님으로 주석 처리됩니다).
이 PEP가 승인될 때 CPython에 더 이상 존재하지 않는 항목은 목록에서 제거됩니다.
아래 체크리스트에 따라 초기 매니페스트에 추가 항목이 포함될 수 있습니다.
Limited API 문서화
“Limited API의 일부”라고 표시하는 메모가, 차용 참조를 반환하는 함수에 대한 메모와 유사한 방식으로 Python 문서에 자동으로 추가됩니다.
Limited API의 모든 구성원에 대한 완전한 목록도 문서에 추가됩니다.
안정 ABI 테스트
안정 ABI에 포함된 모든 심볼을 컴파일 시 사용할 수 있도록 자동 생성된 테스트 모듈이 추가됩니다.
제한 API 변경
제한 API를 변경하기 위한 체크리스트가 Devguide에 추가됩니다. 여기에는 새 항목을 추가하고 기존 항목을 제거하는 작업도 포함됩니다. 이 체크리스트는 1) Python C API 설계의 모범 사례와 일반적인 함정을 언급하고, 2) 제한 API를 변경할 때 수정해야 하는 파일과 실행해야 하는 스크립트를 개발자가 파악하도록 안내합니다.
체크리스트의 초기 제안은 다음과 같습니다. (PEP가 승인된 후에는 현재 버전을 Devguide에서 확인하십시오.)
이 체크리스트는 새로운 변경 사항에 적용된다는 점에 유의하십시오. 기존 제한 API의 여러 항목은 예외적으로 인정된 것이므로 현재는 추가할 수 없습니다.
설계 시 고려 사항:
- 변경으로 인해 3.5 이후 모든 Python 버전의 안정 ABI가 손상되지 않도록 하십시오.
- 외부에 노출되는 이름이 비공개 이름이 아닌지 확인하십시오(즉, 밑줄로 시작하지 않아야 합니다).
- 새 API가 충분히 문서화되었는지 확인하십시오.
- 추가된 함수와 추가된 구조체의 모든 필드에 대한 모든 매개변수 및 반환 값의 타입이 제한 API의 일부이거나 표준 C인지 확인하십시오.
- 새 API와 그 의도된 사용 방식이 현재 지원되는 플랫폼의 기능에만 의존하지 않고 표준 C를 따르는지 확인하십시오. 특히 PEP 7에 지정된 C 방언을 따르십시오.
- 함수 포인터를
void*(데이터 포인터)로 또는 그 반대로 캐스팅하지 마십시오.
- 함수 포인터를
- 새 API가 참조 카운팅 규칙을 따르는지 확인하십시오. (이 규칙을 따르면 API를 더 쉽게 이해하고 다른 Python 구현에서 더 쉽게 사용할 수 있습니다.)
- 함수에서 빌린 참조를 반환하지 마십시오.
- 함수 인자의 참조를 탈취하지 마십시오.
- 해당되는 모든 구조체 필드, 인자 및 반환 값의 소유권 규칙과 수명이 명확히 정의되었는지 확인하십시오.
- 사용자가 쉽게 사용할 수 있도록 고려하십시오. (C에서는 사용 편의성 자체는 그다지 중요하지 않습니다. 실제로 유용한 것은 API를 사용하는 데 필요한 상용구 코드를 줄이는 것입니다. 버그는 상용구 코드 안에 숨기를 좋아합니다.) 버그는 상용구 코드에 숨기를 좋아합니다.)
- 어떤 함수가 특정 인자 값과 함께 자주 호출된다면, 해당 값을 기본값으로 만드는 것을 고려하십시오(
NULL이 전달될 때 사용됩니다).
- 어떤 함수가 특정 인자 값과 함께 자주 호출된다면, 해당 값을 기본값으로 만드는 것을 고려하십시오(
- 향후 확장을 고려하십시오. 예를 들어, 향후 Python 버전에서 구조체에 새 필드를 추가해야 할 가능성이 있다면 어떻게 추가하시겠습니까?
- 향후 CPython 버전에서 변경될 수 있거나 C API 구현마다 다를 수 있는 세부 사항에 대해서는 가능한 한 가정을 적게 하십시오.
- 전역 인터프리터 잠금
- 가비지 컬렉션
- PyObject, 리스트/튜플 및 기타 구조체의 메모리 레이아웃
이러한 지침을 따를 경우 성능이 저하된다면, 제한되지 않은 API에 빠른 함수(또는 매크로)를 추가하고 제한 API에는 안정적인 동등 기능을 추가하십시오.
불명확한 사항이 있거나 지침을 어겨야 할 타당한 이유가 있다면 capi-sig 메일링 리스트에서 변경 사항을 논의하는 것을 고려하십시오.
절차:
- 선언을
Include/바로 아래의 헤더 파일로 이동하여#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03yy0000블록 안에 배치하십시오(yy는 대상 CPython 버전에 해당합니다). - 안정 ABI 매니페스트인
Misc/stable_abi.txt에 항목을 추가하십시오. make regen-all을 사용하여 자동 생성 파일을 재생성하십시오. (또는make를 사용하지 않는 플랫폼을 위한 대안을 사용하십시오.)make check-abi를 사용하여 Python을 빌드하고 검사를 실행하십시오. (또는make를 사용하지 않는 플랫폼을 위한 대안을 사용하십시오.)
확장자 및 임베더를 위한 조언
이 주제에 관한 더 나은 정보 및 우리가 제공하는 보장 사항에 대한 설명과 함께 다음 메모를 문서에 추가합니다:
확장자 작성자는 지원하는 모든 Python 버전으로 테스트해야 하며, 가능하면 해당 버전 중 가장 낮은 버전으로 빌드해야 합니다.
Py_LIMITED_API를 정의하여 컴파일한다고 해서 코드가 Limited API 또는 Stable ABI를 준수한다는 보장은 아닙니다. Py_LIMITED_API는 정의만 다루지만, API에는 예상되는 의미론과 같은 다른 문제도 포함됩니다.
Py_LIMITED_API가 방지하지 못하는 문제의 예는 다음과 같습니다:
- 잘못된 인자로 함수 호출하기
- Python 3.9에서 인자에
NULL값을 허용하기 시작한 함수는 Python 3.8에서NULL이 전달되면 실패합니다. 3.8(또는 그보다 낮은 버전)으로만 테스트해야 이 문제를 발견할 수 있습니다. - 일부 구조체에는 Stable ABI에 포함되는 필드와 포함되지 않는 필드가 몇 개씩 있습니다.
Py_LIMITED_API는 이러한 “비공개” 필드를 걸러 내지 않습니다. - Stable ABI의 일부로 문서화되지는 않았지만
Py_LIMITED_API를 정의해도 노출되는 기능을 사용하는 코드는 향후 중단될 수 있습니다. 팀이 최선을 다하더라도 이러한 문제가 발생할 수 있습니다.
Python 재배포자를 위한 참고 사항
Stable ABI에 대한 약속은 메모리 내 구조체 배치와 함수 호출 규약처럼 컴파일러 및 해당 설정의 영향을 받는 안정적인 하위 ABI 세부 사항에 의존합니다. 이 약속이 유지되려면 특정 플랫폼에서 CPython 3.x 릴리스 간에 이러한 세부 사항이 변경되지 않아야 합니다.
하위 호환성
하위 호환성은 정말 훌륭한 발상입니다!
이 PEP는 기존 Stable ABI 및 Limited API와의 완전한 호환성을 목표로 하지만, 이를 보다 명시적으로 정의합니다. 이는 기존 Stable ABI/Limited API가 무엇인지에 대한 일부 해석과 일치하지 않을 수 있습니다.
보안 관련 사항
알려진 사항이 없습니다.
이 내용을 가르치는 방법
기술 문서는 Doc/c-api/stable에 제공되며 What’s New 문서에서 링크됩니다. CPython 핵심 개발자를 위한 문서는 devguide에 추가됩니다.
참조 구현
issue 43795를 참조하십시오.
향후 아이디어
다음 문제는 이 PEP의 범위에 포함되지 않지만, 향후 가능한 방향을 보여 줍니다.
지원 중단 및 제거를 위한 절차 정의
이 PEP는 Limited API의 일부가 향후 지원 중단되거나 제거될 수 있음을 인정하지만, 이를 위한 절차는 범위에 포함되지 않으며 향후 작성될 수 있는 PEP에 맡깁니다.
ABI 매니페스트를 위한 C 구문
ABI 매니페스트를 C 헤더 파일로 만들거나 매니페스트에서 헤더 파일을 생성하는 것이 유용할 수 있습니다. 다시 말해, 어느 쪽이든 향후 선택 사항입니다.
미해결 문제
현재까지 없습니다.
참고 자료
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.