Python 3의 수명 동안 버전 간 바이너리 호환성을 유지하는 안정적인 ABI와 제한된 API를 정의하는 PEP입니다.
Author:Martin von Löwis <martin at v.loewis.de>Status:Final Type:Standards Track Created:17-May-2009 Python-Version:3.2 Post-History:
목차
현재 각 기능 릴리스는 Windows에서 Python DLL에 새로운 이름을 도입하며, Unix에서는 확장 모듈에 비호환성을 일으킬 수 있습니다. 이 PEP는 Python 3의 수명 동안 사용 가능함이 보장되고, 버전 간에도 바이너리 호환성을 유지하는 안정적인 API 함수 집합을 정의할 것을 제안합니다. 확장 모듈과 Python을 내장하는 애플리케이션은 이 안정적인 ABI로 자신을 제한하는 한 서로 다른 기능 릴리스와 함께 동작할 수 있습니다.
ABI 비호환성의 주된 원인은 메모리 내 구조체 배치의 변경입니다. 예를 들어 문자열 intern 방식이나 객체의 크기를 나타내는 데 사용되는 데이터 형식은 Python 2.x의 수명 동안 변경되었습니다. 그 결과 문자열, 리스트 또는 튜플의 필드에 직접 접근하는 확장 모듈은 재컴파일 없이 더 새로운 버전의 인터프리터에 코드가 로드되면 깨지게 됩니다. 다른 필드의 오프셋이 바뀌어 확장 모듈이 잘못된 데이터에 접근할 수 있기 때문입니다.
일부 경우 비호환성은 프레임 객체나 코드 객체처럼 인터프리터의 내부 객체에만 영향을 줍니다. 예를 들어 줄 번호를 표현하는 방식은 2.x 수명 동안 바뀌었고, 지역 변수를 저장하는 방식도 바뀌었습니다(클로저 도입 때문입니다). 대부분의 애플리케이션은 아마 이런 객체를 전혀 사용하지 않았겠지만, 이를 변경하려면 PYTHON_API_VERSION을 변경해야 했습니다.
Linux에서는 ABI 변경이 흔히 큰 문제가 되지 않습니다. 시스템이 기본 Python 설치를 제공하고, 많은 확장 모듈이 이미 그 버전에 맞게 사전 컴파일되어 제공되기 때문입니다. 추가 모듈이나 추가 Python 버전이 필요하면 사용자는 대개 시스템에서 직접 컴파일할 수 있고, 그 결과 올바른 ABI를 사용하는 모듈이 만들어집니다.
Windows에서는 서로 다른 Python 버전의 여러 설치가 동시에 존재하는 일이 흔하며, 확장 모듈은 최종 사용자가 아니라 작성자가 컴파일합니다. ABI 비호환성의 위험을 줄이기 위해 Python은 현재 ABI 비호환성이 실제로 존재하는지와 관계없이 각 기능 릴리스마다 새로운 DLL 이름 pythonXY.dll을 도입합니다.
이 PEP를 통해 바이너리 확장 모듈의 특정 Python 기능 릴리스 의존성을 줄일 수 있고, Python을 내장하는 애플리케이션도 서로 다른 릴리스와 함께 동작하도록 만들 수 있습니다.
ABI 명세는 두 부분으로 나뉩니다. 하나는 ABI와 함께 사용할 수 있는 함수(군)를 지정하는 API 명세이고, 다른 하나는 어떤 라이브러리와 링크해야 하는지를 지정하는 링크 명세입니다. 실제 ABI(메모리 내 구조체 배치, 함수 호출 규약)는 명시적으로 규정되지 않지만 컴파일러에 의해 암묵적으로 정해집니다. 권고 사항으로, 선택된 플랫폼에 대해서는 특정 ABI를 권장합니다.
Python이 발전하는 동안 새로운 ABI 함수가 추가될 것입니다. 이를 사용하는 애플리케이션은 그에 따라 Python 최소 버전을 요구하게 됩니다. 이 PEP는 Python 라이브러리가 너무 오래된 경우 그러한 애플리케이션이 대체 경로로 동작하게 하는 메커니즘은 제공하지 않습니다.
이 ABI를 사용하려는 애플리케이션과 확장 모듈은 이하에서 모두 “애플리케이션”이라고 통칭합니다.
애플리케이션은 헤더 파일 Python.h만 포함해야 하며(어떤 시스템 헤더보다 먼저 포함), 선택적으로 pyconfig.h를 포함한 다음 Python.h를 포함할 수 있습니다.
애플리케이션을 컴파일하는 동안 전처리기 매크로 Py_LIMITED_API를 정의해야 합니다. 이렇게 하면 ABI의 일부가 아닌 모든 정의가 숨겨집니다.
애플리케이션이 접근할 수 있는 구조체와 구조체 필드는 다음뿐입니다.
이 필드들에 대한 접근자 매크로(Py_REFCNT, Py_TYPE, Py_SIZE)도 애플리케이션에서 사용할 수 있습니다.
다음 타입은 사용 가능하지만 불투명합니다(즉, 미완성형입니다).
타입 객체의 구조는 애플리케이션에 공개되지 않습니다. 따라서 “static” 타입 객체 선언은 더 이상 불가능합니다(이 ABI를 사용하는 애플리케이션의 경우). 대신 타입 객체는 동적으로 생성됩니다. 타입을 쉽게 생성할 수 있도록(특히 함수 포인터를 쉽게 채울 수 있도록) 다음 구조체와 함수를 사용할 수 있습니다.
typedef struct{ int slot; /* 슬롯 id, 아래 참조 */ void pfunc; / 함수 포인터 */ } PyType_Slot;
typedef struct{ const char* name; int basicsize; int itemsize; unsigned int flags; PyType_Slot slots; / slot==0으로 종료. */ } PyType_Spec;
PyObject* PyType_FromSpec(PyType_Spec*);
슬롯을 지정하려면 고유한 슬롯 id를 제공해야 합니다. 새로운 Python 버전은 새로운 슬롯 id를 도입할 수 있지만, 슬롯 id는 절대로 재사용되지 않습니다. 슬롯은 폐기 예정이 될 수는 있지만 Python 3.x 전체에서 계속 지원됩니다.
슬롯 id의 이름은 Python 3.1에서 포인터를 담고 있는 구조체의 필드 이름과 같고, 앞에 Py_ 접두사가 추가됩니다(즉 tp_dealloc 대신 Py_tp_dealloc).
다음 필드는 타입 정의 중에 설정할 수 없습니다: - tp_dict tp_mro tp_cache tp_subclasses tp_weaklist tp_print - tp_weaklistoffset tp_dictoffset
위에 나열한 구조체용 typedef 외에도 다음 typedef를 사용할 수 있습니다. 이것이 ABI에 포함된다는 것은 기본 형식이 한 플랫폼 안에서는 바뀌지 않아야 함을 의미합니다(플랫폼 간에는 다를 수 있습니다).
특히 주목할 점은 Py_UNICODE가 typedef로 제공되지 않는다는 것입니다. 같은 Python 버전이라도 같은 플랫폼에서 이에 대한 정의가 서로 다를 수 있기 때문입니다(좁은 코드 단위를 쓰는지 넓은 코드 단위를 쓰는지에 따라 달라집니다). 유니코드 문자열의 내용에 접근해야 하는 애플리케이션은 이를 wchar_t로 변환할 수 있습니다.
기본적으로 모든 함수는 아래에서 제외되지 않는 한 사용 가능합니다. 함수가 문서화되어 있는지 여부는 중요하지 않습니다.
함수형 매크로(특히 필드 접근 매크로)는 계속 애플리케이션에서 사용할 수 있지만, 함수 호출로 대체됩니다(정의가 ABI의 기능만 참조하는 경우는 예외이며, 예를 들어 여러 _Check 매크로가 그렇습니다).
ABI 함수 선언은 매개변수나 반환형이 변경되지 않습니다. 시그니처 변경이 필요하면 새로운 함수가 도입됩니다. 새로운 함수가 소스 호환적이라면(예를 들어 반환형만 바뀐 경우) 애플리케이션을 다시 컴파일할 때 호출을 새로운 함수로 돌리기 위한 별칭 매크로가 추가될 수 있습니다.
이전 함수를 계속 제공하는 것이 불가능하다면, 그 함수는 폐기 예정이 된 뒤 제거될 수 있고, 그러면 그 함수를 사용하는 애플리케이션은 깨지게 됩니다.
_Py로 시작하는 모든 함수는 애플리케이션에서 사용할 수 없습니다. 또한 애플리케이션에서 사용할 수 없는 매개변수 타입을 기대하는 모든 함수도 ABI에서 제외됩니다. 예를 들어 PyAST_FromNode는 node*를 기대하므로 제외됩니다.
다음 헤더 파일에 선언된 함수는 ABI의 일부가 아닙니다.
또한 FILE*를 기대하는 함수는 ABI의 일부가 아닙니다. 이는 Windows에서 특정 버전의 Microsoft C 런타임 DLL에 의존하는 일을 피하기 위해서입니다.
모듈과 타입의 초기화 함수 및 종료 함수는 사용할 수 없습니다(PyByteArray_Init, PyOS_FiniInterrupts, 그리고 _Fini 또는 _ClearFreeList로 끝나는 모든 함수).
인터프리터 구현 세부 사항을 다루는 여러 함수도 사용할 수 없습니다.
PyStructSequence_InitType는 호출자가 static 타입 객체를 제공해야 하므로 사용할 수 없습니다.
Py_FatalError는 pydebug.h에서 다른 헤더 파일(예: pyerrors.h)로 이동될 예정입니다.
사용 가능한 함수의 정확한 목록은 python3.dll의 Windows 모듈 정의 파일에 나와 있습니다 [1].
타입과 예외를 나타내는 전역 변수는 애플리케이션에서 사용할 수 있습니다. 또한 매크로에서 참조되는 일부 선택된 전역 변수(Py_True, Py_False 등)도 사용할 수 있습니다.
전역 변수 정의의 전체 목록은 python3.def 파일에 나와 있습니다 [1]. 여기서 DATA로 선언된 항목은 변수입니다.
심볼릭 상수를 정의하는 모든 매크로는 애플리케이션에서 사용할 수 있으며, 숫자 값은 변경되지 않습니다.
추가로 다음 매크로를 사용할 수 있습니다.
버퍼 인터페이스(Py_buffer 타입, bf_getbuffer와 bf_releasebuffer 타입 슬롯 등)는 현재 시점에서 Py_buffer 구조체의 안정성이 명확하지 않기 때문에 ABI에서 제외되었습니다. 향후 릴리스에서 ABI 포함을 검토할 수 있습니다.
현재 여러 함수는 호출자가 보통 PyObject*를 가지고 있음에도 특정 구조체를 기대합니다. 이들은 매개변수로 PyObject*를 기대하도록 변경되었습니다. 이로 인해 현재 매개변수 타입으로 명시적 캐스팅을 하는 애플리케이션에서는 경고가 발생할 수 있습니다. 해당 함수는 PySlice_GetIndices, PySlice_GetIndicesEx, PyUnicode_AsWideChar, PyEval_EvalCode입니다.
Windows에서 애플리케이션은 python3.dll과 링크해야 하며, import library python3.lib가 제공됩니다. 이 DLL은 /export 링커 옵션을 통해 자신의 모든 API 함수를 전체 인터프리터 DLL, 즉 python3y.dll로 리디렉션합니다.
Unix 시스템에서 ABI는 보통 python 실행 파일 자체가 제공합니다. 확장 모듈이 Py_LIMITED_API로 컴파일된 경우 PyModule_Create는 API 버전으로 3을 전달하도록 변경되며, API 버전 검사에서는 3 또는 현재 PYTHON_API_VERSION을 모두 적합한 값으로 받아들입니다. Python이 공유 라이브러리로 컴파일된 경우 libpython3.so와 libpython3.y.so로 모두 설치되며, 이 PEP를 따르는 애플리케이션은 전자와 링크해야 합니다(확장 모듈은 계속 libpython 공유 객체 없이 링크하고 대신 런타임 링크에 의존할 수 있습니다). ABI 버전은 PYTHON_ABI_VERSION으로 기호적으로 제공됩니다.
또한 Unix에서는 PEP 3149 태그 abi<PYTHON_ABI_VERSION>가 확장 모듈 파일 이름에서 허용됩니다. 이렇게 이름 붙은 파일이 실제로 제한된 API만 사용하도록 제한되어 있는지에 대한 검사는 수행되지 않으며, distutils 코드 동결 때문에 distutils에 이러한 파일을 빌드하는 지원도 추가되지 않습니다.
이 PEP는 분기에서 구현될 예정이며 [2], 이를 통해 사용자는 자신의 모듈이 ABI를 준수하는지 확인할 수 있습니다. 사용자가 타입 정의를 다시 작성하지 않도록, 타입 정의를 포함한 C 소스 코드를 변환하는 스크립트도 제공될 예정입니다 [3].
이 문서는 퍼블릭 도메인에 두었습니다.