Python의 C API 변경하기¶
C API는 다음 계층으로 나뉩니다:
Py_BUILD_CORE가 정의되어 있을 때 사용할 수 있는 내부 비공개 API입니다. 이상적으로는Include/internal/에 선언합니다. 이름이 밑줄로 시작하는 모든 API도 비공개로 간주합니다.PyUnstable_이름 접두사로 식별되는 불안정 C API입니다. 이상적으로는 일반 공개 API와 함께 Include/cpython/에 선언합니다.관련 Include/Python.h를 일반적인 방식으로 포함할 때 사용할 수 있는 “일반” 공개 C API입니다. 이상적으로는
Include/cpython/에 선언합니다.관련
Py_LIMITED_API가 정의되어 있을 때 사용할 수 있는 제한 C API입니다. 이상적으로는Include/바로 아래에 선언합니다.
각 계층에는 정의를 추가하거나 변경할 때 고려해야 할 서로 다른 안정성 및 유지관리 요구 사항이 있습니다.
공개 C API의 공개 하위 호환성 보장은 사용자 문서인 Doc/c-api/stable.rst (C API Stability)에 설명되어 있습니다. C 언어 호환성 보장은 Doc/c-api/intro.rst (Introduction)에 설명되어 있습니다.
코어 개발자는 공개적으로 약속하는 수준보다 호환성에 더 주의해야 합니다. 자세한 내용은 공개 C API를 참조하십시오.
내부 API¶
내부 API는 Include/internal/에 정의되며, Py_BUILD_CORE와 같은 매크로가 나타내듯이 CPython 자체를 빌드할 때만 사용할 수 있습니다.
내부 API는 언제든지 변경할 수 있지만, 다른 API나 다른 CPython 개발자가 이에 의존할 수 있으므로 안정적으로 유지하는 것이 좋습니다. 사용자에게 내부 API는 때때로 까다로운 문제를 해결하는 최선의 우회 방법입니다. — 하지만 지원되는 방식으로 이러한 사용 사례를 처리할 방법을 찾을 수 있도록 해당 사용 사례를 C API Discourse category나 이슈에서 논의해야 합니다.
PyAPI_FUNC 또는 PyAPI_DATA 사용¶
PyAPI_FUNC 또는 PyAPI_DATA로 정의된 Include/internal/ 내의 함수나 구조체는 디버거나 프로파일러와 같은 특정 사용 사례에만 노출되는 내부 함수입니다. 이상적으로는 이를 불안정 C API로 마이그레이션해야 합니다.
extern 키워드 사용¶
extern 키워드로 정의된 Include/internal/ 내의 함수는 CPython 코드베이스 외부에서 사용해서는 안 되며 사용할 수도 없습니다. Py_BUILD_CORE_BUILTIN 매크로를 정의하여 빌드한 내장 표준 라이브러리 확장만 이러한 함수를 사용할 수 있습니다.
확실하지 않다면 새로운 내부 C 함수는 extern 키워드를 사용하여 Include/internal에 정의해야 합니다.
비공개 이름¶
이름이 밑줄로 시작하는 모든 API도 내부 API로 간주합니다. 현재는 정의를 Include/internal/에 배치하거나 .c 파일에 직접 배치하는 대신 이러한 이름을 사용하는 주요 사용 사례가 하나뿐입니다:
사용자가 직접 호출해서는 안 되는, 다른 공개 API를 위한 내부 도우미입니다.
역사적으로는 불안정 C API가 더 적합한 API에도 밑줄을 사용했다는 점에 유의하십시오:
새 API의 실제 사용을 테스트하기 위해 Python 릴리스에 포함되는 “잠정적” API;
JIT 컴파일러처럼 매우 특수한 용도를 위한 API입니다.
내부 API 테스트¶
내부 C API의 C 테스트는 Modules/_testinternalcapi.c에 있습니다. 이름이 test_*인 함수는 테스트로 직접 사용됩니다. 테스트의 Python 부분은 Lib/test 내 여러 위치에 있습니다.
공개 C API¶
CPython의 공개 C API는 Python.h를 일반적인 방식으로 포함할 때(즉, 다른 변형을 선택하는 매크로를 정의하지 않을 때) 사용할 수 있습니다.
제한된 API의 일부가 아니라면 아래에서 설명하는 대로 Include/cpython/에 정의해야 합니다.
새로운 공개 API를 추가하기 전에 C API 워킹 그룹의 decisions repo에서 문의하십시오. 이는 새로 추가된 API가 일관되고 유지 관리 가능한지 확인하는 데 도움이 됩니다.
또한 C99에 없는 C 기능을 요구하기 전에 C API WG와 협의하십시오. 공개 문서는 C11과의 호환성만 보장하지만, 실제로는 필요한 경우에만 C11 기능을 개별적으로 도입합니다.
공개 API 확장/변경 지침¶
새 API가 참조 횟수 계산 규칙을 따르는지 확인하십시오. (이 규칙을 따르면 API의 동작을 더 쉽게 추론할 수 있고, 다른 Python 구현에서도 더 쉽게 사용할 수 있습니다.)
함수는 참조를 절대로 훔쳐서는 안 됩니다.
함수는 빌린 참조를 절대로 반환해서는 안 됩니다.
참조를 반환하는 함수는 반드시 강한 참조를 반환해야 합니다.
해당하는 모든 구조체 필드, 인자 및 반환 값의 소유권 규칙과 수명이 명확히 정의되었는지 확인하십시오.
PyObject *를 반환하는 함수는 성공 시 유효한 포인터를 반환해야 하며, 오류 시 예외를 발생시키고NULL을 반환해야 합니다. 대부분의 다른 API는 오류 시 예외를 발생시키고-1을 반환해야 하며, 성공 시0을 반환해야 합니다.대소 결과를 반환하는 API는 더 작은 결과에 대해
0을 반환하고, 더 큰 결과에 대해1을 반환해야 합니다. 다음과 같이 세 가지 반환 값을 갖는 조회 함수를 살펴보겠습니다.return -1: 내부 오류 또는 API 오용이며, 예외가 발생합니다.return 0: 조회에 성공했지만 항목을 찾지 못했습니다.return 1: 조회에 성공했으며 항목을 찾았습니다.
이 지침을 API에 적용할 수 없다면 공개 논의를 시작하십시오.
참고
반환 값이란 C 반환 문이 반환하는 값을 뜻합니다.
C API 테스트¶
공개 C API의 테스트는 _testcapi 모듈에 있습니다. 이름이 test_*인 함수는 테스트로 직접 사용됩니다. Python 코드가 필요하거나 일부를 Python으로 작성하는 편이 더 쉬운 테스트는 Lib/test 내에 있으며, 주로 Lib/test/test_capi에 있습니다.
크기 때문에 _testcapi 모듈은 여러 소스 파일에 정의되어 있습니다. 새로운 테스트 세트를 추가하려면(또는 단일 Modules/_testcapimodule.c에서 테스트 세트를 추출하려면):
Modules/_testcapi/yourfeature.cC 파일을 생성하십시오.다음 사항을 제외하고, 이 파일은 평소와 같이 모듈을 정의해야 합니다.
<Python.h>대신"parts.h"를 포함하십시오.PyInit_modname대신_testcapi모듈을 받아 함수/클래스를 추가하는_PyTestCapi_Init_yourfeature함수를 정의하십시오. (함수를 추가하려면PyModule_AddFunctions를 사용할 수 있습니다.)
_PyTestCapi_Init_*함수를Modules/_testcapi/parts.h에 추가하십시오.Modules/_testcapimodule.c의PyInit__testcapi에서_PyTestCapi_Init_*를 호출하십시오.다른
_testcapi/*.c항목과 함께 새 C 파일을 Modules/Setup.stdlib.in, PCbuild/_testcapi.vcxproj, PCbuild/_testcapi.vcxproj.filters에 추가하십시오.
모든 Modules/_testcapi/*.c 소스가 같은 모듈을 초기화하므로 이름 충돌에 주의하십시오.
기존 테스트를 옮길 때 실제로 사용자 정의 예외를 테스트하는 경우가 아니라면 TestError를 PyExc_AssertionError로 자유롭게 대체해도 됩니다.
불안정 C API¶
불안정 C API 계층은 디버거 및 JIT 컴파일러처럼 인터프리터와 긴밀하게 통합해야 하는 확장을 위한 것입니다. 이 계층의 사용자는 기능 릴리스마다 코드를 변경해야 할 수 있습니다.
여러 측면에서 이 계층은 일반 C API와 비슷합니다.
Python.h를 일반적인 방식으로 포함하면 사용할 수 있습니다.관련 Include/cpython/에 정의해야 합니다.
의도치 않게 손상하지 않도록 테스트가 필요합니다.
예상되는 동작에 관해 개발자와 사용자 모두가 합의할 수 있도록 문서가 필요합니다.
동일한 방식으로 테스트되고 문서화됩니다.
차이점은 다음과 같습니다.
함수, 구조체, 매크로 등의 이름은
PyUnstable_접두사로 시작합니다. 이를 통해 불안정 계층에 속하는 항목을 정의합니다.불안정 API는 폐지 예정 기간 없이 기능 릴리스에서 변경될 수 있습니다.
문서에 안정성 관련 참고 사항이 표시됩니다. 이는 이름을 기반으로 자동으로 이루어집니다(Doc/tools/extensions/c_annotations.py를 통함).
“불안정”이라는 이름에도 불구하고 서드파티 코드가 이 API를 안정적으로 사용할 수 있도록 하는 규칙이 있습니다.
기능 릴리스(
3.x.0및3.x.0의 알파와 베타 포함)에서 변경 및 제거할 수 있습니다.기존 기능을 위한 새로운 불안정 API는 베타 기능 동결 이후에도 첫 번째 릴리스 후보까지 추가할 수 있습니다. 베타 기간에는 Core Development Discourse에서 합의해야 합니다.
하위 호환성이 없는 변경은 기존 C 호출자의 컴파일이 실패하게 해야 합니다. 예를 들어 인자를 추가하거나 제거하거나, 함수 이름을 변경해야 합니다.
API를 불안정 계층으로 옮기거나 불안정 계층에서 다른 계층으로 옮길 때는 호환되지 않는 변경이 이루어질 때까지 이전 이름을 계속 사용할 수 있어야 하지만 지원 중단 예정으로 표시해야 합니다. 다시 말해 호출 코드를 중단시킬 수는 있지만, 불필요하게 중단시켜서는 안 됩니다.
API를 공개 계층에서 불안정 계층으로 옮기기¶
PyUnstable_접두사를 붙인 새 이름으로 API를 노출하십시오. 모든 심볼(함수, 매크로, 변수 등)에PyUnstable_접두사를 사용해야 합니다.이전 이름을 별칭으로 만드십시오(예: 새 함수를 호출하는
static inline함수).일반적으로
Py_DEPRECATED를 사용하여 이전 이름을 지원 중단 예정으로 표시하십시오.“새 소식”에 변경 사항을 공지하십시오.
호환되지 않는 변경이 이루어질 때까지 이전 이름을 계속 사용할 수 있어야 합니다. 파이썬의 하위 호환성 정책(PEP 387)에 따라, 이 폐지 예정은 최소 두 번의 릴리스 동안 지속되어야 합니다(운영 위원회의 예외는 제외).
공식 불안정 계층이 추가된 Python 3.12 이전 버전에서 도입된 API에는 완화된 규칙이 적용됩니다. Python 3.12 이전에 도입되었으며 다음 중 하나에 해당하는 API에는 해당 함수가 이미 불안정 계층의 일부였던 것처럼 호환되지 않는 변경을 적용하고 이전 이름을 제거할 수 있습니다.
기본값보다 안정성이 낮다고 문서화되어 있습니다.
이름이 밑줄로 시작합니다.
API를 비공개 계층에서 불안정 계층으로 옮기기¶
PyUnstable_접두사를 붙인 새 이름으로 API를 노출하십시오.이전 이름이 문서화되어 있거나 외부에서 널리 사용된다면 별칭으로 만들고 지원 중단 예정으로 표시하십시오(일반적으로
Py_DEPRECATED를 사용합니다). 이전에 공개되어 있었던 것처럼 호환되지 않는 변경이 이루어질 때까지 계속 사용할 수 있어야 합니다.이는 밑줄로 시작하는 이름에도 적용됩니다. Python이 항상 선행 밑줄을 엄격하게 다룬 것은 아닙니다.
새 소식에 변경 사항을 공지하십시오.
API를 불안정 계층에서 공개 계층으로 옮기기¶
PyUnstable_접두사가 없는 새 이름으로 API를 노출하십시오.이전
PyUnstable_*이름을 별칭으로 만드십시오(예: 새 함수를 호출하는static inline함수).새 소식에 변경 사항을 공지하십시오.
새 공개 이름이 지원 중단 예정으로 표시되거나 제거될 때까지 이전 이름을 계속 사용할 수 있어야 합니다. 이전 이름은 처음부터 불안정했으므로 지원 중단 예정으로 표시할 필요가 없지만, 어떤 함수가 이제 더 광범위한 사용자를 대상으로 제공할 준비가 되었다는 이유만으로 작동하는 코드를 중단시킬 필요도 없습니다.
제한된 API¶
제한된 API는 Python 3 버전 간의 ABI 안정성을 보장하도록 설계된 C API의 하위 집합입니다. Py_LIMITED_API 매크로를 정의하면 노출되는 API가 이 하위 집합으로 제한됩니다.
안정 ABI를 깨뜨리는 변경은 허용되지 않습니다.
제한된 API는 cpython 및 internal 하위 디렉터리를 제외한 Include/ 안에 정의해야 합니다.
제한된 API를 변경하고 항목을 제거하기 위한 지침¶
안정 ABI를 위반해서는 안 되지만, 다음 조건을 충족하면 기존 제한된 API를 변경하고 항목을 제거할 수 있습니다:
하위 호환성 정책(PEP 387)을 준수하고,
안정 ABI를 위반하지 않아야 합니다 – 즉, 이전 Python 버전의 제한된 API로 컴파일된 확장이 최신 Python 버전에서도 계속 작동해야 합니다.
이는 처리하기 까다로우며 신중하게 생각해야 합니다. 몇 가지 예는 다음과 같습니다:
제한된 API의 어느 버전에서든 매크로를 통해 접근하는 함수, 구조체 등은 밑줄로 시작하는 이름을 사용하더라도 안정 ABI의 일부입니다. 이러한 항목을 제거해서는 안 되며 시그니처도 변경해서는 안 됩니다. (단, 구현은 변경할 수 있습니다.)
구조체 멤버가 제한된 API의 어느 버전에든 포함된 적이 있다면 순서를 바꿀 수 없습니다.
제한된 API에서 사용자가 구조체를 직접 할당할 수 있도록 허용한다면 그 크기를 변경해서는 안 됩니다.
내보낸 심볼(함수 및 데이터)은 내보낸 심볼로 계속 사용할 수 있어야 합니다. 구체적으로, Python이 실제 함수도 계속 제공하는 경우에만 함수를
static inline함수(또는 매크로)로 변환할 수 있습니다. 예시는 3.10의Py_NewRefmacro 및 redefinition을 참조하십시오.
안정 ABI의 일부로 표시된 항목도 제거할 수 있지만, 이전 제한된 API의 어떤 버전에서도 해당 항목을 사용할 방법이 없었던 경우에만 가능합니다.
제한된 API에 추가하기 위한 지침¶
일반적인 공개 C API에 대한 지침이 적용됩니다. 관련 공개 API 확장/변경 지침를 참조하십시오.
새 제한된 API는
Py_LIMITED_API가 해당 API가 추가된 버전 이상으로 설정된 경우에만 정의해야 합니다. (올바른#if가드는 아래를 참조하십시오.)모든 매개변수 타입, 반환값, 구조체 멤버 등은 제한된 API의 일부여야 합니다.
FILE*(또는 ABI 이식성 문제가 있는 다른 타입)을 다루는 함수는 추가해서는 안 됩니다.
매크로를 정의할 때는 다시 한번 신중하게 생각하십시오.
매크로는 구현 세부 사항을 노출해서는 안 됩니다
함수는 함수형 매크로로만 제공하지 말고 실제 함수로 내보내야 합니다.
가능하면 매크로를 사용하지 마십시오. 그러면 C 전처리기를 사용하지 않는 언어에서도 제한된 API를 더 쉽게 사용할 수 있습니다.
제한된 API를 확장하기 전에 공개 논의를 시작하십시오
제한된 API는 현재 지원되는 플랫폼의 기능만이 아니라 표준 C를 준수해야 합니다. 정확한 C 방언은 PEP 7에 설명되어 있습니다.
문서 예제(더 일반적으로는 API의 의도된 사용법)도 표준 C를 준수해야 합니다.
특히 함수 포인터를
void*(데이터 포인터)로 캐스팅하거나 그 반대로 캐스팅하지 마십시오.
사용자가 쉽게 사용할 수 있는지 고려하십시오.
C에서는 사용 편의성 자체가 그다지 중요하지 않으며, 유용한 것은 API를 사용하는 데 필요한 상용구 코드를 줄이는 것입니다. 버그는 상용구 코드에 숨어들기 쉽습니다.
함수가 특정 인자 값으로 자주 호출될 예정이라면, 그 값을 기본값으로 설정하는 것을 고려하십시오(
NULL이 전달될 때 사용됩니다).Limited API는 문서화가 잘되어 있어야 합니다.
향후 확장을 고려하십시오
향후 Python 버전에서 구조체에 새 필드를 추가해야 할 가능성이 있다면, 그렇게 할 수 있도록 하십시오.
향후 CPython 버전에서 변경되거나 C API 구현마다 다를 수 있는 구현 세부 사항에 대해서는 가능한 한 적게 가정하십시오. 가장 중요한 CPython 고유의 구현 세부 사항은 다음과 관련됩니다:
GIL
가비지 수집
PyObject, 리스트/튜플 및 기타 구조체의 메모리 레이아웃
이 지침을 따르면 성능이 저하되는 경우, 비제한 API에는 빠른 함수(또는 매크로)를 추가하고 Limited API에는 이에 상응하는 안정적인 항목을 추가하십시오.
불분명한 점이 있거나 지침을 어겨야 할 타당한 이유가 있다면, Discourse의 C API category에서 변경 사항을 논의하는 것을 고려하십시오.
Limited API에 새 정의 추가하기¶
Include/바로 아래의 헤더 파일에 다음과 같이 보호된 블록 안에 선언을 추가하십시오:#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03yy0000yy는 대상 CPython 버전에 해당하며, 예를 들어 Python 3.10의 경우0x030A0000입니다.Stable ABI 매니페스트인
Misc/stable_abi.toml에 항목을 추가하십시오make regen-limited-abi를 사용하여 자동 생성 파일을 다시 생성하십시오.make가 없는 플랫폼에서는 다음 명령을 직접 실행하십시오:./python ./Tools/build/stable_abi.py --generate-all ./Misc/stable_abi.toml
Python을 빌드하고
make check-limited-abi를 사용하여 검사하십시오.make가 없는 플랫폼에서는 다음 명령을 직접 실행하십시오:./python ./Tools/build/stable_abi.py --all ./Misc/stable_abi.toml
테스트를 추가하십시오 – 아래를 참조하십시오.
Limited API 테스트¶
Limited API는 C API의 하위 집합이므로 개별 함수의 동작을 테스트할 필요가 없습니다. 대신 테스트에서는 노출된 하위 집합을 사용하여 어떤 작업을 수행할 수 있는지 확인하거나, 현재 Limited API에서는 제거되었지만 이전 Limited API/Stable ABI 버전에서는 계속 지원해야 하는 기능을 실행해 볼 수 있습니다.
테스트 파일을 추가하려면:
Modules/_testcapi/yourfeature_limited.cC 파일을 추가하십시오. 해당 파일이 이미 있지만Py_LIMITED_API버전이 너무 낮다면, Python 3.12+용yourfeature_limited_3_12.c처럼 버전 접미사를 추가하십시오.#define Py_LIMITED_API를 필요한 최소 Limited API 버전으로 정의하십시오.Py_LIMITED_API정의 뒤에#include "parts.h"를 추가하십시오호환되지 않는 빌드에서 건너뛰도록 파일의 나머지 전체를
#ifdef LIMITED_API_AVAILABLE로 감싸십시오.C API tests에 대한 일반 지침을 따르십시오. 모든 추가 사항은
#ifdef LIMITED_API_AVAILABLE로 보호되는 섹션에 배치하십시오.
Lib/test의 Python 테스트에는 test.support.requires_limited_api 데코레이터를 사용하여 호환되지 않는 빌드에서 건너뛰도록 하십시오.