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

Python 개선 제안 한국어 번역

PEP 733 – Python의 공개 C API 평가

Author:
Erlend Egeberg Aasland <erlend at python.org>, Domenico Andreoli <domenico.andreoli at linux.com>, Stefan Behnel <stefan_ml at behnel.de>, Carl Friedrich Bolz-Tereick <cfbolz at gmx.de>, Simon Cross <hodgestar at gmail.com>, Steve Dower <steve.dower at python.org>, Tim Felgentreff <tim.felgentreff at oracle.com>, David Hewitt <1939362+davidhewitt at users.noreply.github.com>, Shantanu Jain <hauntsaninja at gmail.com>, Wenzel Jakob <wenzel.jakob at epfl.ch>, Irit Katriel <irit at python.org>, Marc-Andre Lemburg <mal at lemburg.com>, Donghee Na <donghee.na at python.org>, Karl Nelson <nelson85 at llnl.gov>, Ronald Oussoren <ronaldoussoren at mac.com>, Antoine Pitrou <solipsis at pitrou.net>, Neil Schemenauer <nas at arctrix.com>, Mark Shannon <mark at hotpy.org>, Stepan Sindelar <stepan.sindelar at oracle.com>, Gregory P. Smith <greg at krypto.org>, Eric Snow <ericsnowcurrently at gmail.com>, Victor Stinner <vstinner at python.org>, Guido van Rossum <guido at python.org>, Petr Viktorin <encukou at gmail.com>, Carol Willing <willingc at gmail.com>, William Woodruff <william at yossarian.net>, David Woods <dw-git at d-woods.co.uk>, Jelle Zijlstra <jelle.zijlstra at gmail.com>
Status:
Final
Type:
Informational
Created:
16-Oct-2023
Post-History:
01-Nov-2023

Table of Contents

번역·라이선스 안내

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

초록

정보 제공용 PEP는 공개 C API에 대한 우리의 공통된 관점을 설명합니다. 이 문서는 다음을 정의합니다.

  • C API의 목적
  • 이해관계자와 각자의 구체적인 사용 사례 및 요구 사항
  • C API의 강점
  • 9개의 약점 영역으로 분류한 C API의 문제

이 문서는 식별된 문제에 대한 해결책을 제안하지 않습니다. C API 문제의 공통 목록을 작성함으로써, 이 문서는 변경 제안에 관한 지속적인 논의를 이끌고 평가 기준을 식별하는 데 도움을 줍니다.

서론

Python의 C API는 현재 수행하는 여러 목적을 위해 설계되지 않았습니다. 이는 처음에는 인터프리터의 C 코드와 Python 언어 및 라이브러리 사이의 내부 API였던 것에서 발전했습니다. 첫 번째 형태에서는 Python을 C/C++ 애플리케이션에 임베드하고 C/C++로 확장 모듈을 작성할 수 있도록 공개되었습니다. 이러한 기능은 Python 생태계의 성장에 중요한 역할을 했습니다. 수십 년에 걸쳐 C API는 서로 다른 수준의 안정성을 제공하도록 성장했고, 관례가 바뀌었으며, C/C++ 이외의 언어에 대한 바인딩과 같은 새로운 사용 패턴이 등장했습니다. 향후 몇 년 동안 GIL 제거와 JIT 컴파일러 개발 같은 새로운 발전이 C API를 더욱 시험할 것으로 예상됩니다. 그러나 이러한 성장은 명확하게 문서화된 지침의 지원을 받지 못했으며, 그 결과 CPython의 서로 다른 서브시스템에서 API 설계에 일관되지 않은 접근 방식이 나타났습니다. 또한 CPython은 더 이상 Python의 유일한 구현이 아니며, CPython만이 유일한 구현이었던 시기에 내려진 설계 결정 중 일부는 다른 구현이 다루기 어렵습니다 [Issue 64]. 그동안 교훈을 얻었고 C API의 설계와 구현 모두에서 실수가 식별되었습니다.

C API의 발전은 하위 호환성 제약과 기술적·사회적 측면 모두에서의 고유한 복잡성이 결합되어 있기 때문에 어렵습니다. 서로 다른 유형의 사용자는 서로 다르고 때로는 상충하는 요구 사항을 제시합니다. 안정성과 발전 사이의 절충은 점진적인 개선을 위한 제안이 나올 때 지속적이고 매우 논쟁적인 논의 주제입니다. C API의 개선, 재설계 또는 대체를 위한 여러 제안이 제시되었으며, 각각은 문제에 대한 심층적인 분석을 나타냅니다. 2023년 Language Summit에서는 C API의 서로 다른 측면에 세 개의 연속 세션이 할애되었습니다. 새로운 설계가 지난 30년 동안 C API에 누적된 문제를 해결하는 동시에, 원래 설계되지 않았던 사용 사례에 맞게 C API를 업데이트할 수 있다는 데에는 일반적인 합의가 있습니다.

그러나 Language Summit에서는 우리가 해결하려는 문제에 대한 명확한 공통 이해 없이 해결책을 논의하려 하고 있다는 인식도 있었습니다. 우리는 제안된 해결책을 평가할 수 있기 전에 C API의 현재 문제에 대해 합의해야 한다고 결정했습니다. 따라서 우리는 이 문제에 대한 모든 사람의 아이디어를 수집하기 위해 GitHub에 capi-workgroup 저장소를 만들었습니다.

해당 저장소에는 60개가 넘는 서로 다른 이슈가 생성되었으며, 각 이슈는 C API의 문제를 설명합니다. 이를 분류하고 여러 반복적으로 나타나는 주제를 확인했습니다. 아래 섹션은 대부분 이러한 주제에 해당하며, 각 섹션에는 해당 범주에서 제기된 이슈에 대한 종합적인 설명과 개별 이슈 링크가 포함되어 있습니다. 또한 C API의 서로 다른 이해관계자와 각 이해관계자가 가진 구체적인 요구 사항을 파악하는 것을 목표로 하는 섹션도 포함했습니다.

C API 이해관계자

서론에서 언급했듯이 C API는 원래 CPython 인터프리터와 Python 계층 사이의 내부 인터페이스로 만들어졌습니다. 이후 서드파티 개발자가 Python 프로그램을 확장하고 임베드할 수 있는 방법으로 공개되었습니다. 수년에 걸쳐 서로 다른 요구 사항과 중점 영역을 가진 새로운 유형의 이해관계자가 등장했습니다. 이 섹션에서는 서로 다른 이해관계자가 C API를 통해 수행해야 하는 작업을 기준으로 이러한 복잡한 상황을 설명합니다.

모든 이해관계자를 위한 공통 작업

일반적이며 모든 유형의 API 사용자가 필요로 하는 작업이 있습니다:

  • 함수를 정의하고 호출합니다
  • 새로운 타입을 정의합니다
  • 내장 타입과 사용자 정의 타입의 인스턴스를 생성합니다
  • 객체 인스턴스에 대한 작업을 수행합니다
  • 타입, 인스턴스, 함수를 비롯한 객체를 검사합니다
  • 예외를 발생시키고 처리합니다
  • 모듈을 임포트합니다
  • Python의 OS 인터페이스에 접근합니다

다음 섹션에서는 다양한 이해관계자의 고유한 요구 사항을 살펴봅니다.

확장 기능 작성자

확장 기능 작성자는 C API의 전통적인 사용자입니다. 이들의 요구 사항은 위에 나열된 공통 작업입니다. 또한 일반적으로 다음 작업이 필요합니다:

  • 새로운 모듈을 생성합니다
  • C 수준에서 모듈 간 인터페이스를 효율적으로 구성합니다

임베디드 Python 애플리케이션 작성자

Python 인터프리터가 임베드된 애플리케이션입니다. 예로는 BlenderOBS가 있습니다.

다음 작업을 수행할 수 있어야 합니다:

  • 인터프리터를 구성합니다(임포트 경로, inittab, sys.argv, 메모리 할당자 등).
  • 인터프리터의 정상적인 종료와 재시작을 비롯하여 실행 모델 및 프로그램 수명과 상호 작용합니다.
  • 깊은 복사본을 생성하지 않고도 Python이 사용할 수 있는 방식으로 복잡한 데이터 모델을 표현합니다.
  • 동결된 모듈을 제공하고 가져옵니다.
  • 여러 독립적인 인터프리터를 실행하고 관리합니다(특히 전역 효과를 피하려는 라이브러리에 임베드된 경우에 해당합니다).

Python 구현

CPython, PyPy, GraalPy, IronPython, RustPython, MicroPython, Jython과 같은 Python 구현은 서로 다른 서브시스템의 구현에 매우 다른 접근 방식을 취할 수 있습니다. 다음이 필요합니다.

  • API가 추상적이고 구현 세부 사항을 숨겨야 합니다.
  • 호환성을 보장하는 테스트 스위트가 포함된 API 사양이 이상적입니다.
  • Python 구현 간에 공유할 수 있는 ABI가 있으면 좋습니다.

대체 API 및 바인딩 생성기

C API를 직접 사용해 프로그래밍하는 것보다 확장 기능 사용자에게 이점을 제공하는 C API의 대안을 구현하는 프로젝트가 여러 개 있습니다. 이러한 API는 C API로 구현되며, 일부 경우에는 CPython 내부 기능을 사용해 구현됩니다.

Python과 다른 객체 모델, 패러다임 또는 언어 사이에 바인딩을 생성하는 라이브러리도 있습니다.

이러한 범주 사이에는 중복되는 부분이 있습니다. 바인딩 생성기는 대개 대체 API를 제공하며, 그 반대도 마찬가지입니다.

예로는 C++용 Cython, cffi, pybind11nanobind, Rust용 PyO3, Qt용 PySide가 사용하는 Shiboken, GTK용 PyGObject, Go용 Pygolo, Java용 JPype, Android용 PyJNIus, Objective-C용 PyObjC, C/C++용 SWIG, .NET(C#)용 Python.NET, HPy, Mypyc, Pythranpythoncapi-compat가 있습니다. 함수 인자를 구문 분석하기 위한 CPython의 DSL인 Argument Clinic은 이 이해관계자 범주에 속하는 것으로 볼 수도 있습니다.

대체 API에는 CPython에 효율적으로 접근하기 위한 최소한의 구성 요소가 필요합니다. 일반적으로 사람이 읽도록 의도되지 않은 코드를 생성하므로, 반드시 인체공학적인 API가 필요하지는 않습니다. 그러나 성능을 희생하지 않으면서 내부 기능에 접근하지 않을 수 있을 만큼 충분히 포괄적이어야 합니다.

바인딩 생성기에는 흔히 다음이 필요합니다.

  • 가능한 한 동등한 Python 코드의 동작과 일치하는 사용자 지정 객체(예: 함수/모듈 객체 및 트레이스백 항목)를 생성합니다.
  • 기존 C 확장에서 정적인 객체(예: 클래스/모듈)를 동적으로 생성하고, CPython이 해당 객체의 상태와 수명을 관리하도록 합니다.
  • 외부 객체(문자열, GC 대상 컨테이너)를 낮은 오버헤드로 동적으로 조정합니다.
  • 외부 메커니즘, 실행 모델 및 보장을 Python 방식에 맞게 조정합니다(스택풀 코루틴, 컨티뉴에이션, 단일 작성자 또는 다중 리더 의미론, 가상 다중 상속, 1 기반 인덱싱, 매우 긴 상속 체인, 고루틴, 채널 등).

이러한 도구는 더 안정적인 API와 더 빠른 API(더 낮은 수준일 수도 있음) 중에서 선택할 수 있으면 이점을 얻을 수도 있습니다. 그러면 사용자는 코드를 자주 재생성할 여유가 있는지, 또는 더 안정적이고 유지 관리 작업이 적은 대신 일부 성능을 포기할 수 있는지를 결정할 수 있습니다.

C API의 강점

이 문서의 대부분은 새로운 설계에서 수정되기를 바라는 C API의 문제에 할애되어 있지만, C API의 강점을 지적하고 이를 보존해야 한다는 점도 중요합니다.

서론에서 언급했듯이 C API는 지난 30년 동안 Python 생태계의 개발과 성장을 가능하게 했으며, 원래 설계 목적에 포함되지 않았던 사용 사례를 지원하도록 발전해 왔습니다. 이러한 실적 자체가 C API가 얼마나 효과적이고 가치 있었는지를 보여줍니다.

capi-workgroup 논의에서는 몇 가지 구체적인 강점이 언급되었습니다. 힙 타입은 정적 타입보다 훨씬 안전하고 사용하기 쉬운 것으로 확인되었습니다 [Issue 4].

Python 문자열을 기반으로 조회할 때 C 문자열 리터럴을 받는 API 함수는 매우 편리합니다 [Issue 30].

제한된 API는 구현 세부 정보를 숨기는 API를 사용하면 Python을 더 쉽게 발전시킬 수 있음을 보여 줍니다 [Issue 30].

C API 문제

이 문서의 나머지 부분에서는 capi-workgroup 저장소에 보고된 문제를 요약하고 분류합니다. 이슈는 여러 범주로 그룹화되어 있습니다.

API 발전 및 유지 관리

C API를 변경하기 어렵다는 점이 이 보고서의 핵심입니다. 이는 여기서 논의하는 많은 이슈에 암묵적으로 포함되어 있으며, 특히 점진적인 버그 수정으로 문제를 해결할 수 있는지 아니면 API 재설계의 일부로만 해결할 수 있는지를 결정해야 할 때 그러합니다 [Issue 44]. 각각의 점진적인 변경으로 얻는 이점은 변경에 따른 혼란을 정당화하기에는 너무 작다고 여겨지는 경우가 많습니다. 시간이 지나면 이는 API의 설계나 구현에서 저지른 모든 실수가 무기한 우리에게 남는다는 것을 의미합니다.

이 문제에 대해서는 두 가지 관점이 있을 수 있습니다. 하나는 이것이 해결해야 할 문제이며, 우리가 설계하는 새로운 C API에는 API 요소의 사용 중단과 제거를 포함하는 점진적인 API 발전 프로세스의 형태로 해결책이 내장되어야 한다는 관점입니다. 다른 가능한 접근 방식은 이것이 해결해야 할 문제가 아니라 모든 API의 특성이라는 관점입니다. 이 관점에서는 API 발전이 점진적으로 이루어져서는 안 되며, 과거의 실수에서 교훈을 얻고 하위 호환성 요구 사항에 얽매이지 않는 대규모 재설계를 통해 이루어져야 합니다(그동안 새로운 API 요소를 추가할 수는 있지만, 어떤 요소도 절대 제거할 수는 없습니다). 절충적인 접근 방식은 이 두 극단의 중간에 있으며, 점진적으로 처리할 수 있을 만큼 쉽거나 중요한 문제는 수정하고 나머지는 그대로 두는 것입니다.

CPython에서 우리가 가진 문제는 API 발전에 대해 합의된 공식적인 접근 방식이 없다는 것입니다. 핵심 팀의 구성원마다 서로 다른 방향으로 나아가고 있으며, 이는 지속적인 의견 불일치의 원인이 되고 있습니다. 새로운 C API에는 해당 API의 유지 관리가 따를 모델에 대한 명확한 결정과 함께, 이를 운영할 기술적·조직적 프로세스가 수반되어야 합니다.

모델에 API의 점진적인 발전을 위한 조항이 포함된다면, 변경이 사용자에게 미치는 영향을 관리하는 프로세스도 포함될 것이며 [Issue 60], 외부 하위 호환성 모듈을 도입하거나 [Issue 62], “승인된” 함수로 구성된 새로운 API 계층을 도입하는 방식일 수 있습니다 [Issue 55].

API 사양 및 추상화

C API에는 공식 사양이 없으며, 현재는 특정 버전의 참조 구현(CPython)에 포함된 내용이 곧 정의입니다. 문서는 불완전한 설명 역할만 하므로 전체 API, 제한된 API 또는 안정 ABI의 정확성을 검증하기에 충분하지 않습니다. 그 결과 C API는 더 눈에 띄는 사양 업데이트 없이 릴리스 간에 크게 변경될 수 있으며, 이로 인해 여러 문제가 발생합니다.

C/C++ 이외의 언어를 위한 바인딩은 C 코드를 구문 분석해야 합니다 [Issue 7]. 일부 C 언어 기능은 컴파일러에 따라 달라지는 출력(예: 열거형)을 생성하거나 단순한 파서가 아니라 C 전처리기/컴파일러를 필요로 하기 때문에(예: 매크로) 이러한 방식으로 처리하기 어렵습니다 [Issue 35].

또한 C 헤더 파일은 공개 API의 일부로 포함할 의도가 없는 것까지 노출하는 경향이 있습니다 [Issue 34]. 특히 내부 데이터 구조의 정확한 메모리 레이아웃과 같은 구현 세부 정보가 노출될 수 있습니다 [Issue 22PEP 620]. 이는 API 발전을 매우 어렵게 만들 수 있으며, 특히 안정 ABI에서 발생할 때 그러합니다. ob_refcntob_type의 경우처럼 이러한 값은 참조 카운팅 매크로를 통해 접근됩니다 [Issue 45].

참조 카운팅이 노출되는 방식과 관련하여 더 근본적인 문제도 확인했습니다. C 확장이 Py_INCREFPy_DECREF 호출을 통해 참조를 관리하도록 요구하는 방식은 CPython의 메모리 모델에 특화되어 있으며, 대체 Python 구현에서 이를 에뮬레이트하기 어렵습니다. [Issue 12]

PyObject*가 핸들이 아니라 실제 포인터로 C API에 노출된다는 사실에서도 또 다른 문제가 발생합니다. 객체의 주소가 해당 객체의 ID 역할을 하며 비교에 사용되므로, GC 중에 객체를 이동시키는 대체 Python 구현에서는 문제가 복잡해집니다 [Issue 37].

별개의 문제는 객체 참조가 런타임에 불투명하며, 고유한 목적을 가진 tp_traverse/tp_clear 호출을 통해서만 발견할 수 있다는 점입니다. 런타임이 객체 그래프의 구조를 파악하고 그 변경 사항을 계속 추적할 방법이 있다면, 이를 통해 대체 구현이 서로 다른 메모리 관리 방식을 구현할 수 있습니다 [Issue 33].

객체 참조 관리

함수의 참조 의미론을 명확하게 드러내는 일관된 명명 규칙이 없으며, 이로 인해 일반적인 동작을 따르지 않는 오류 발생 가능성이 높은 C API 함수가 만들어집니다. C API 함수가 PyObject*를 반환하면 호출자는 일반적으로 해당 객체에 대한 참조의 소유권을 획득합니다. 그러나 함수가 “차용된” 참조를 반환하는 예외도 있으며, 이 경우 호출자는 해당 참조에 접근할 수 있지만 그 참조를 소유하지는 않습니다. 마찬가지로 함수는 일반적으로 인자에 대한 참조의 소유권을 변경하지 않지만, 함수가 참조를 “훔치는” 예외가 있습니다. 즉, 호출을 통해 참조의 소유권이 호출자에서 피호출자로 영구적으로 이전됩니다 [Issue 8Issue 52]. 문서에서 이러한 상황을 설명하는 용어도 개선할 수 있습니다 [Issue 11].

“차용된” 참조를 반환하는 함수(예: PyList_GetItem) [Issue 5Issue 21] 또는 객체 내부 구조의 일부에 대한 포인터(예: PyBytes_AsString) [Issue 57]의 경우에는 더욱 근본적인 변경이 필요합니다. 두 경우 모두 참조/포인터는 소유 객체가 해당 참조를 보유하는 동안 유효하지만, 이 기간을 추론하기는 어렵습니다. 이러한 함수를 안전하게 만들 수 있는 메커니즘 없이는 API에 존재해서는 안 됩니다.

컨테이너의 경우, 현재 API에는 포함된 객체의 참조에 대한 일괄 연산이 없습니다. 이는 INCREFDECREF를 매크로로 사용할 수 없는 안정 ABI에서 특히 중요하며, 함수 호출의 연속으로 구현하면 일괄 연산의 비용이 커지기 때문입니다 [Issue 15].

타입 정의 및 객체 생성

C API에는 PyTuple_NewPyUnicode_New와 같이 불완전하거나 일관되지 않은 Python 객체를 생성할 수 있게 하는 함수가 있습니다. 객체가 GC에 의해 추적되거나 해당 객체의 tp_traverse/tp_clear 함수가 호출될 때 이로 인해 문제가 발생합니다. 이와 관련된 문제로는 부분적으로 초기화된 튜플을 수정하는 데 사용되는 PyTuple_SetItem과 같은 함수가 있습니다(튜플은 완전히 초기화되면 변경할 수 없습니다) [Issue 56].

타입 정의 API에서 몇 가지 문제를 확인했습니다. 레거시상의 이유로 tp_newtp_vectorcall 사이에 상당한 코드 중복이 자주 발생합니다 [Issue 24]. 타입 슬롯 함수는 간접적으로 호출되어야 하며, 그래야 해당 함수의 시그니처를 변경하여 컨텍스트 정보를 포함할 수 있습니다 [Issue 13]. 타입 객체의 서로 다른 필드를 초기화하고 정리하는 책임이 프로세스의 어느 단계에 있는지와 같이, 타입 정의 및 생성 프로세스의 여러 측면이 명확하게 정의되어 있지 않습니다 [Issue 49].

오류 처리

C API의 오류 처리는 스레드 상태에 저장되는 오류 표시기(전역 범위)에 기반합니다. 설계 의도는 각 API 함수가 오류가 발생했는지를 나타내는 값을 반환하는 것이었습니다(관례상 -1 또는 NULL입니다). 프로그램이 오류가 발생했음을 알게 되면 오류 표시기에 저장된 예외 객체를 가져올 수 있습니다. 오류 처리와 관련된 여러 문제를 확인했으며, 잘못 사용하기 너무 쉬운 API를 지적했습니다.

실행 중 발생하는 모든 오류를 보고하지 않는 함수가 있습니다. 예를 들어 PyDict_GetItem은 키의 해시 함수를 호출할 때 또는 딕셔너리에서 조회를 수행할 때 발생하는 모든 오류를 지웁니다 [Issue 51].

Python 코드는 정의상 처리 중인 예외가 있는 상태로 실행되지 않으며, 일반적으로 네이티브 함수를 사용하는 코드도 오류가 발생하면 중단되어야 합니다. 대부분의 C API 함수에서는 이를 확인하지 않으며, 인터프리터에는 예외가 설정된 상태에서 오류 처리 코드가 C API 함수를 호출하는 부분이 있습니다. 예를 들어 _PyErr_WriteUnraisableMsg의 오류 처리기에서 PyUnicode_FromString을 호출하는 부분을 참조하십시오 [Issue 2].

값을 반환하지 않는 함수가 있으므로, 호출자는 오류가 발생했는지 확인하기 위해 오류 표시자를 조회해야 합니다. 예를 들면 PyBuffer_Release [Issue 20]가 있습니다. 반환값이 있는 다른 함수들도 있지만, 이 반환값만으로는 오류가 발생했는지를 명확하게 알 수 없습니다. 예를 들어, PyLong_AsLong은 오류가 발생한 경우 또는 인자의 값이 실제로 -1인 경우 -1을 반환합니다 [Issue 1]. 두 경우 모두 함수가 호출되기 전에 오류 표시자가 이미 설정되어 있었을 가능성이 있고 오류가 잘못 귀속될 수 있으므로 API는 오류가 발생하기 쉽습니다. 호출 전에 오류를 감지하지 못한 것은 호출 코드의 버그이지만, 이 경우 프로그램의 동작은 문제를 식별하고 디버깅하기 어렵게 만듭니다.

PyObject* 인자를 받으며 NULL일 때 특별한 의미를 가지는 함수들이 있습니다. 예를 들어, PyObject_SetAttr가 설정할 값으로 NULL을 받으면 해당 속성을 지워야 한다는 의미입니다. NULL이 값 생성 중 발생한 오류를 나타내는데 프로그램이 이 오류를 확인하지 못했을 수 있으므로 오류가 발생하기 쉽습니다. 프로그램은 NULL을 오류와는 다른 의미로 잘못 해석합니다 [Issue 47].

API 계층 및 안정성 보장

서로 다른 API 계층은 안정성과 API 발전, 때로는 성능 사이에서 서로 다른 절충점을 제공합니다.

안정적인 ABI는 검토가 필요한 영역으로 확인되었습니다. 현재는 불완전하며 널리 채택되지 않았습니다. 동시에 안정적인 ABI의 존재로 인해 일부 구현 세부 사항을 변경하기가 어려워지고 있습니다. ob_refcnt, ob_type, ob_size와 같은 구조체 필드를 노출하기 때문입니다. 안정적인 ABI를 유지할 가치가 있는지에 관한 논의가 있었습니다. 양측의 주장은 [Issue 4] 및 [Issue 9]에서 확인할 수 있습니다.

또는 안정적인 ABI를 발전시킬 수 있으려면 동일한 Python 바이너리에서 여러 버전을 지원하는 메커니즘이 필요하다는 제안이 있었습니다. 단일 ABI 버전 내에서 개별 함수를 버전 관리하는 것만으로는 충분하지 않다는 지적이 있었습니다. 서로 상호 운용되는 함수 그룹을 함께 발전시켜야 할 수 있기 때문입니다 [Issue 39].

제한된 API는 자주 변경될 가능성이 낮은 고품질 API로 제한하여 사용하려는 사용자에게 권장되는 C API의 승인된 하위 집합으로 3.2에서 도입되었습니다. Py_LIMITED_API 플래그를 사용하면 사용자가 프로그램을 제한된 API의 이전 버전으로 제한할 수 있지만, 이제는 이전 버전을 제외하는 반대 옵션이 필요합니다. 이를 통해 제한된 API의 결함이 있는 요소를 교체하여 발전시킬 수 있습니다 [Issue 54]. 보다 일반적으로 재설계 시에는 API 계층을 지정하는 방식을 재검토하고, 현재 서로 다른 계층 중에서 선택하는 방식을 통합할 방법을 설계하는 것을 고려해야 합니다 [Issue 59].

이름이 밑줄로 시작하는 API 요소는 비공개로 간주되며, 본질적으로 안정성이 보장되지 않는 API 계층에 해당합니다. 그러나 이는 최근에야 PEP 689에서 명확해졌습니다. PEP 689보다 앞서 존재하는 이러한 API 요소에 어떤 변경 정책을 적용해야 하는지는 명확하지 않습니다 [Issue 58].

오류 검사를 수행하는 안전한 버전과 함께 안전하지 않지만 빠른 버전도 제공하는 API 함수들이 있습니다(예: PyTuple_GET_ITEMPyTuple_GetItem). 이를 자체 계층으로 그룹화할 수 있으면 도움이 될 수 있습니다. 즉, “unsafe API” 계층과 “safe API” 계층입니다 [Issue 61].

C 언어 사용

CPython이 C 언어를 사용하는 방식과 관련하여 여러 문제가 제기되었습니다. 우선 어떤 C 방언을 사용하는지, 해당 방언과의 호환성을 어떻게 테스트하는지, 그리고 API 헤더가 C++ 방언과 호환되는지에 관한 문제가 있습니다 [Issue 42].

현재 API에서 const의 사용은 제한적이지만, 이를 변경하는 것을 고려해야 하는지는 명확하지 않습니다 [Issue 38].

longint C 타입을 현재 사용하고 있으며, int32_tint64_t와 같은 고정 너비 정수가 이제 더 나은 선택일 수 있습니다 [Issue 27].

매크로, 가변 인자, 열거형, 비트필드 및 함수가 아닌 기호와 같이 다른 언어가 상호작용하기 어려운 C 언어 기능을 사용하고 있습니다 [Issue 35].

더 구체적인 타입이어야 하는 PyObject*인자를 받는 API 함수가 있습니다(예를 들어, 인자가 PyTupleObject*가 아니면 실패하는 PyTuple_Size가 있습니다). 이것이 좋은 패턴인지, 아니면 API가 더 구체적인 타입을 기대해야 하는지는 아직 결정되지 않았습니다 [Issue 31].

PyDict_GetItemString과 같이 구체적인 타입을 받는 함수가 API에 있으며, 이 함수는 PyObject*대신 C 문자열로 지정된 키에 대해 딕셔너리 조회를 수행합니다. 한편, PyDict_ContainsString에 대해서는 구체적인 타입 대안을 추가하는 것이 적절하지 않다고 여겨집니다. 이와 관련된 원칙은 지침에 문서화해야 합니다 [Issue 23].

구현상의 결함

아래에는 특정 부분에 국한된 구현상의 결함 목록이 나와 있습니다. 대부분은 그렇게 하기로 한다면 점진적으로 수정할 수 있을 것입니다. 어떤 경우든 새로운 API 설계에서는 이러한 결함을 피해야 합니다.

성공에는 0을, 실패에는 -1을 반환하는 관례를 따르지 않는 함수가 있습니다. 예를 들어, PyArg_ParseTuple은 성공 시 0을, 실패 시 0이 아닌 값을 반환합니다 [Issue 25].

Py_CLEARPy_SETREF 매크로는 인자를 두 번 이상 참조하므로, 인자가 부작용이 있는 식이면 해당 식이 중복 평가됩니다 [Issue 3].

Py_SIZE의 의미는 타입에 따라 달라지며 항상 신뢰할 수 있는 것은 아닙니다 [Issue 10].

일부 API 함수는 이에 대응하는 Python 함수와 동일하게 동작하지 않습니다. PyIter_Next의 동작은 tp_iternext와 다릅니다. [Issue 29]. PySet_Contains의 동작은 set.__contains__와 다릅니다 [Issue 6].

PyArg_ParseTupleAndKeywords가 const가 아닌 char* 배열을 인자로 받기 때문에 사용하기가 더 어려워집니다 [Issue 28].

Python.h는 전체 API를 노출하지 않습니다. marshal.h를 비롯한 일부 헤더는 Python.h에서 포함되지 않습니다. [Issue 43].

이름 지정

PyLongPyUnicode가 나타내는 Python 타입(int/str)과 더 이상 일치하지 않는 이름을 사용합니다. 이는 새로운 API에서 수정할 수 있습니다 [Issue 14].

API에 Py/_Py 접두사가 없는 식별자가 있습니다 [Issue 46].

누락된 기능

이 절은 현재 C API에 누락된 것으로 확인된 기능에 대한 요청, 즉 기능 요청 목록으로 구성되어 있습니다.

디버그 모드

재컴파일 없이 활성화할 수 있고 다양한 유형의 오류를 감지하는 데 도움이 되는 여러 검사를 활성화하는 디버그 모드입니다 [Issue 36].

인트로스펙션

현재 C로 정의된 객체에 대해서는 Python 객체에 대해 가능한 것과 같은 신뢰할 수 있는 인트로스펙션 기능이 없습니다 [Issue 32].

힙 타입에 대한 효율적인 타입 검사입니다 [Issue 17].

다른 언어와의 상호 작용을 개선합니다

다른 GC 기반 언어와 인터페이스하고 해당 언어의 GC를 Python의 GC와 통합합니다 [Issue 19].

트레이스백에 외부 스택 프레임을 삽입합니다 [Issue 18].

다른 언어에서 사용할 수 있는 구체적인 문자열입니다 [Issue 16].

참조

  1. Python/C API 참조 매뉴얼
  2. 2023 언어 서밋 블로그 게시물: C API에 관한 세 가지 강연
  3. GitHub의 capi-workgroup
  4. C API 워크그룹에 관한 Irit의 Core Sprint 2023 슬라이드
  5. Petr의 Core Sprint 2023 슬라이드
  6. HPy에서 배울 내용에 관한 HPy 팀의 Core Sprint 2023 슬라이드
  7. Core Sprint 2023 Python C API 강연에 관한 Victor의 슬라이드
  8. Python의 안정성 약속 — Cristián Maureira-Fredes, PySide 유지 관리자
  9. 안정 ABI로 전환할 때 5년 전에 PySide에 있었던 문제에 관한 보고서