PEP 743 – Python C API에 Py_OMIT_LEGACY_API 추가
- Author:
- Victor Stinner <vstinner at python.org>, Petr Viktorin <encukou at gmail.com>
- Discussions-To:
- Discourse thread
- Status:
- Rejected
- Type:
- Standards Track
- Created:
- 11-Mar-2024
- Python-Version:
- 3.15
- Post-History:
- 11-Mar-2024, 27-Jul-2024
- Resolution:
- 20-Feb-2026
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
거부 통지
Steering Council은 현재 형식의 이 PEP를 거부했습니다:
PEP가 해결하려는 문제가 해결할 가치가 있다는 점에는 동의하지만, 이것이 올바른 접근 방식이라고 확신하지는 않습니다.
the full post를 참조하십시오.
초록
폐기되었거나 소프트 폐기된 기호를 숨기는 Py_OMIT_LEGACY_API C 매크로를 추가하여, 다른 API가 해결하는 알려진 문제가 있는 API를 사용하지 않도록 선택할 수 있게 합니다.
또한 Py_ 접두사가 없는 API에 네임스페이스가 지정된 대안을 추가하고, 원래 이름을 소프트 폐기합니다.
동기
Python C API의 일부에는 나중에야 명확해지는 결함이 있습니다.
API가 기능이나 최적화의 추가를 방해하거나 심각한 보안 위험 또는 유지 관리 부담을 초래하는 경우, PEP 387에 설명된 대로 해당 API를 폐기하고 제거할 수 있습니다.
그러나 이로 인해 “날카로운 모서리”가 있는 API가 일부 남습니다. 이러한 API는 현재 사용자에게는 잘 작동하지만 새 코드에서는 피해야 합니다. 예를 들면 다음과 같습니다:
- 예외를 알릴 수 없는 API이므로, 실패가 무시되거나 치명적 오류와 함께 프로세스가 종료됩니다. 예를 들어
PyObject_HasAttr입니다. - 예를 들어 변경 가능한 객체에서 참조를 빌리거나 아직 완성되지 않은 변경 가능한 객체를 노출하는 등, 스레드로부터 안전하지 않은 API입니다. 예를 들어
PyDict_GetItemWithError입니다. Py/_Py접두사를 사용하지 않는 이름을 가진 API이므로 다른 코드와 충돌할 수 있습니다. 예를 들면 다음과 같습니다:setter.
이러한 결함에도 불구하고 일반적으로 API를 올바르게 사용할 수 있다는 점을 알아두는 것이 중요합니다. 예를 들어 단일 스레드 환경에서는 스레드 안전성이 문제가 되지 않습니다. 일부 또는 심지어 대부분의 다른 컨텍스트에서는 잘못된 API를 사용하더라도, 작동하는 코드를 중단하고 싶지는 않습니다.
반면, 특히 더 안전한 대안이 존재하는 경우에는 새로운 코드에서 사용자가 그러한 “바람직하지 않은” API를 사용하지 않도록 유도하고자 합니다.
Py 접두사 추가
CPython 헤더에 정의된 일부 이름은 네임스페이스가 지정되지 않았습니다. 즉, Py 접두사(또는 그 변형인 _Py 및 대체 대소문자 표기)가 없습니다. 예를 들어 단순히 setter라는 이름의 함수 타입을 선언합니다.
이러한 이름은 ABI에 내보내지지 않지만(make smelly로 확인됨), 사용자 코드와 충돌할 수 있으며, 더 중요하게는 서드 파티 확장에 연결된 라이브러리와 충돌할 수 있습니다.
네임스페이스가 지정된 별칭을 제공하고 이러한 이름을 (소프트) 폐기하는 것도 가능하지만, 서드 파티 코드와 충돌하지 않게 하는 유일한 방법은 Python 헤더에 이러한 이름을 전혀 정의하지 않는 것입니다.
근거
사용자가 원할 경우 “바람직하지 않은” API를 쉽게 피할 수 있도록 하고자 합니다.
이 작업은 서드파티 린터에 맡기는 것으로 충분할 수도 있습니다. 그러려면 그러한 린터에 (소프트) 디프리케이트된 API 목록을 제공할 수 있는 좋은 방법이 필요합니다. 이를 추가하는 동안, 추가 도구가 필요하지 않도록 CPython 헤더에서 린터의 작업을 직접 상당히 쉽게 수행할 수 있습니다. Python과 달리 C에서는 사용자가 “opt-in” 매크로를 정의하도록 하여 전체 프로젝트 또는 각 개별 소스 파일에 대해 사용 가능한 API를 제한하기가 상당히 쉽습니다.
이미 Py_LIMITED_API를 사용하여 이와 유사한 작업을 수행하고 있으며, 이는 사용 가능한 API를 안정 ABI로 컴파일되는 부분 집합으로 제한합니다. (돌이켜보면 이러한 종류의 제한에는 다른 매크로 이름을 사용했어야 했지만, 지금은 이를 변경하기에는 너무 늦었습니다.)
분명히 하자면, 이 메커니즘은 디프리케이션을 대체하는 수단이 아닙니다. 디프리케이션은 새로운 기능이나 최적화를 방해하거나 보안 위험 또는 유지 관리 부담을 초래하는 API에 적용합니다. 반면 이 메커니즘은 “작업을 수행하는 약간 더 나은 방법을 찾았습니다”라고 할 수 있는 경우를 위한 것입니다. 즉, 오용하기 더 어렵거나 단순히 덜 오해를 불러일으키는 이름을 사용하는 경우입니다. (가벼운 이야기로는, 많은 사람이 함수 사이의 빈 줄 개수에 대해 잔소리하도록 코드 품질 검사기를 설정합니다. 이들이 더 중요한 “코드 냄새”를 식별하도록 도와줍시다!)
제안된 매크로는 API 정의를 변경하지 않고, 단지 이를 숨깁니다. 따라서 코드가 이 매크로를 사용하여 컴파일된다면, 동일한 동작으로 이 매크로 없이도 컴파일됩니다. 이는 핵심 개발자에게 다음과 같은 영향을 미칩니다. 바람직하지 않은 동작을 처리하려면 새롭고 더 나은 API를 도입한 다음에 기존 API 사용을 억제해야 합니다. 이에 따라 단일 종류의 문제를 해결하기 위해 코드베이스 전체를 훑기보다는 개별 API를 살펴보고 알려진 모든 문제를 한 번에 수정해야 하며, 이를 통해 동일한 함수의 이름을 여러 번 변경하는 일을 피해야 합니다.
사양
Py_OMIT_LEGACY_API매크로를 도입합니다. #include <Python.h>보다 먼저 이 매크로가 정의되면, 아래에 설명된 일부 API 정의가 Python 헤더 파일에서 제외됩니다.
이 매크로는 <Python.h>에서 노출되는 완전한 최상위 정의만 제외합니다. 그 밖의 요소(ABI, 구조체 정의, 매크로 확장, 정적 인라인 함수 본문 등)는 영향을 받지 않습니다.
C API 작업 그룹(PEP 731)은 제외되는 정의 집합에 대한 권한을 가집니다.
제외되는 정의 집합은 CPython의 특정 기능 릴리스에 연결되며, 각 3.x.0 Beta 1 릴리스에서 확정됩니다. 드문 경우에는 항목을 언제든지 제거하여(즉, 사용할 수 있도록 하여)도 됩니다.
제외되는 API의 요구 사항
Py_OMIT_LEGACY_API로 제외되는 API는 다음 조건을 충족해야 합니다:
- 소프트 디프리케이트되어야 합니다(PEP 387 참조).
- 해당 API의 알려진 모든 사용 사례에 대해 문서화된 대안 또는 해결 방법이 있어야 합니다.
- 계속 작동하는지 확인하는 테스트가 있어야 합니다(
#define또는typedef를 사용한 1:1 이름 변경은 제외). - 문서화되어야 합니다(이전 버전의 문서에서 언급된 적이 전혀 없는 경우는 제외). 그리고
- C API 작업 그룹의 승인을 받아야 합니다. (WG는 관련 API 그룹에 포괄적인 승인을 부여할 수 있으며, 예시는 아래의 Initial set을 참조하십시오.)
Py_OMIT_LEGACY_API는 더 나은 대안으로 간단히 대체할 수 있는 API를 위한 것임에 유의하십시오. 대체 수단이 없는 API는 일반적으로 대신 디프리케이트해야 합니다.
위치
Py_OMIT_LEGACY_API로 제외되는 모든 API 정의는 새 헤더인 Include/legacy.h로 이동됩니다.
이 내용은 린터 작성자가 목록을 작성하여 API를 오류가 아닌 경고로 표시할 수 있도록 돕기 위한 것입니다.
소스 전용 구성 요소(매크로, 타입)를 단순히 이름 변경하는 경우에는 대체 항목을 추가하는 동일한 버전 – 또는 동일한 PR – 에서 이름이 생략될 것으로 예상합니다. 즉, 원래 정의의 이름을 변경하고 이전 이름에 대한 typedef 또는 #define을 Include/legacy.h에 추가합니다.
문서
생략된 API에 대한 문서는 일반적으로 다음과 같아야 합니다:
- 권장 대체 항목 뒤에 나타나고,
- 대체 항목을 참조하며(예: “X와 유사하지만…”),
- 대체 항목과의 차이점 및 마이그레이션 지침에 중점을 둡니다.
타당한 이유가 있다면 예외를 적용할 수 있습니다.
초기 목록
다음 API는 Py_OMIT_LEGACY_API가 설정된 상태에서 생략됩니다:
- 빌린 참조를 반환하는 API 생략:
생략되는 API 대체 항목 PyDict_GetItem()PyDict_GetItemRef()PyDict_GetItemString()PyDict_GetItemStringRef()PyImport_AddModule()PyImport_AddModuleRef()PyList_GetItem()PyList_GetItemRef() - 사용 중단 API 생략:
생략되는 사용 중단 API 대체 항목 PY_FORMAT_SIZE_T"z"PY_UNICODE_TYPEwchar_tPyCode_GetFirstFree()PyUnstable_Code_GetFirstFree()PyCode_New()PyUnstable_Code_New()PyCode_NewWithPosOnlyArgs()PyUnstable_Code_NewWithPosOnlyArgs()PyImport_ImportModuleNoBlock()PyImport_ImportModule()PyMem_DEL()PyMem_Free()PyMem_Del()PyMem_Free()PyMem_FREE()PyMem_Free()PyMem_MALLOC()PyMem_Malloc()PyMem_NEW()PyMem_New()PyMem_REALLOC()PyMem_Realloc()PyMem_RESIZE()PyMem_Resize()PyModule_GetFilename()PyModule_GetFilenameObject()PyOS_AfterFork()PyOS_AfterFork_Child()PyObject_DEL()PyObject_Free()PyObject_Del()PyObject_Free()PyObject_FREE()PyObject_Free()PyObject_MALLOC()PyObject_Malloc()PyObject_REALLOC()PyObject_Realloc()PySlice_GetIndicesEx()(두 번 호출합니다. 현재 문서를 참조하십시오.) PyThread_ReInitTLS()(더 이상 필요하지 않습니다.) PyThread_create_key()PyThread_tss_alloc()PyThread_delete_key()PyThread_tss_free()PyThread_delete_key_value()PyThread_tss_delete()PyThread_get_key_value()PyThread_tss_get()PyThread_set_key_value()PyThread_tss_set()PyUnicode_AsDecodedObject()PyUnicode_Decode()PyUnicode_AsDecodedUnicode()PyUnicode_Decode()PyUnicode_AsEncodedObject()PyUnicode_AsEncodedString()PyUnicode_AsEncodedUnicode()PyUnicode_AsEncodedString()PyUnicode_IS_READY()(더 이상 필요하지 않습니다.) PyUnicode_READY()(더 이상 필요하지 않습니다.) PyWeakref_GET_OBJECT()PyWeakref_GetRef()PyWeakref_GetObject()PyWeakref_GetRef()Py_UNICODEwchar_t_PyCode_GetExtra()PyUnstable_Code_GetExtra()_PyCode_SetExtra()PyUnstable_Code_SetExtra()_PyDict_GetItemStringWithError()PyDict_GetItemStringRef()_PyEval_RequestCodeExtraIndex()PyUnstable_Eval_RequestCodeExtraIndex()_PyHASH_BITSPyHASH_BITS_PyHASH_IMAGPyHASH_IMAG_PyHASH_INFPyHASH_INF_PyHASH_MODULUSPyHASH_MODULUS_PyHASH_MULTIPLIERPyHASH_MULTIPLIER_PyObject_EXTRA_INIT(더 이상 필요하지 않음) _PyThreadState_UncheckedGet()PyThreadState_GetUnchecked()_PyUnicode_AsString()PyUnicode_AsUTF8()_Py_HashPointer()Py_HashPointer()_Py_T_OBJECT( tp_getset; 문서는 작성 예정)_Py_WRITE_RESTRICTED(더 이상 필요하지 않음) - API를 소프트 사용 중단하고 생략합니다:
제외된 사용 중단 API 대체 항목 PyDict_GetItemWithError()PyDict_GetItemRef()PyDict_SetDefault()PyDict_SetDefaultRef()PyMapping_HasKey()PyMapping_HasKeyWithError()PyMapping_HasKeyString()PyMapping_HasKeyStringWithError()PyObject_HasAttr()PyObject_HasAttrWithError()PyObject_HasAttrString()PyObject_HasAttrStringWithError() <structmember.h>레거시 API를 제외하십시오:<Python.h>에서 포함되지 않으며 별도로 포함해야 하는 헤더 파일structmember.h은Py_OMIT_LEGACY_API가 정의되어 있으면#error를 발생시킵니다. 다음 API가 영향을 받습니다:제외된 사용 중단 API 대체 항목 T_SHORTPy_T_SHORTT_INTPy_T_INTT_LONGPy_T_LONGT_FLOATPy_T_FLOATT_DOUBLEPy_T_DOUBLET_STRINGPy_T_STRINGT_OBJECT( tp_getset; 문서 작성 예정)T_CHARPy_T_CHART_BYTEPy_T_BYTET_UBYTEPy_T_UBYTET_USHORTPy_T_USHORTT_UINTPy_T_UINTT_ULONGPy_T_ULONGT_STRING_INPLACEPy_T_STRING_INPLACET_BOOLPy_T_BOOLT_OBJECT_EXPy_T_OBJECT_EXT_LONGLONGPy_T_LONGLONGT_ULONGLONGPy_T_ULONGLONGT_PYSSIZETPy_T_PYSSIZETT_NONE( tp_getset; 문서 작성 예정)READONLYPy_READONLYPY_AUDIT_READPy_AUDIT_READREAD_RESTRICTEDPy_AUDIT_READPY_WRITE_RESTRICTED(더 이상 필요하지 않습니다) RESTRICTEDPy_AUDIT_READ- 소프트 디프리케이트된 매크로를 생략합니다:
생략된 매크로 대체 항목 Py_IS_NAN()isnan()(C99 이상<math.h>)Py_IS_INFINITY()isinf(X)(C99 이상<math.h>)Py_IS_FINITE()isfinite(X)(C99 이상<math.h>)Py_MEMCPY()memcpy()(C<string.h>) Py/_Py접두사가 없는 typedef(getter,setter,allocfunc, …)를 소프트 디프리케이트하고 생략하며, 접두사를 추가한 새로운typedef(Py_getter, 등)를 대신 사용합니다.Py/_Py접두사가 없는 매크로(METH_O,CO_COROUTINE,FUTURE_ANNOTATIONS,WAIT_LOCK, …)를 소프트 디프리케이트하고 생략하며, 접두사를 추가한 새로운매크로(Py_METH_O, 등)를 대신 사용합니다.- C API 작업 그룹이 승인한 기타 항목입니다.
이러한 제안된 대체 항목이나 관련 문서가 3.14.0b1까지 추가되지 않으면, Py_OMIT_LEGACY_API의 이후 버전에서 생략됩니다. (configure에서 생성되는 매크로인 HAVE_*, WITH_*, ALIGNOF_*, SIZEOF_*와 공통 접두사가 없는 몇 가지 매크로가 이에 해당할 것으로 예상합니다.)
구현
미정입니다.
하위 호환성
매크로는 하위 호환성이 있습니다. 개발자는 자신의 속도로 매크로를 도입하고 업데이트할 수 있으며, 한 번에 하나의 소스 파일에 적용할 수도 있습니다.
향후 CPython 버전에서는 Py_OMIT_LEGACY_API가 숨기는 집합에 더 많은 API를 추가하여 사용자 코드를 손상시킬 수 있습니다. 해결 방법은 매크로의 정의를 해제하거나(안전하게 수행할 수 있습니다) 코드를 재작업하는 것입니다.
논의
- PEP 743 – Python C API에 Py_COMPAT_API_VERSION 추가하기 (2차) (2024년 7월)
- 대규모 이름 변경 마무리하기 (2024년 5월)
- PEP 743: Python C API에 Py_COMPAT_API_VERSION 추가하기 (2024년 3월)
- C API Evolutions: 사용 중단된 함수를 숨기는 매크로(2023년 10월)
- C API Problems: 새로운 깔끔한 API를 위한 옵트인 매크로? 알려진 문제가 없는 함수 서브셋(2023년 6월)
선행 사례
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.