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

Python 개선 제안 한국어 번역

PEP 384 – 안정 ABI 정의

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:


Table of Contents

번역·라이선스 안내

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

Important

This PEP is a historical document. The up-to-date, canonical documentation can now be found at C API and ABI Stability (user docs) and Changing Python’s C API (개발 문서).

×

See PEP 1 for how to propose changes.

초록

현재는 기능 릴리스마다 Windows의 Python DLL 이름이 새로 바뀌며, Unix에서는 확장 모듈에 비호환성이 생길 수 있습니다. 이 PEP는 Python 3의 수명 동안 사용할 수 있음이 보장되고 버전 간에 바이너리 호환성도 유지되는 안정적인 API 함수 집합을 정의할 것을 제안합니다. 확장 모듈과 Python을 내장하는 애플리케이션은 이 안정 ABI만 사용하는 한 여러 기능 릴리스에서 동작할 수 있습니다.

근거

ABI 비호환성의 주된 원인은 메모리 내 구조체의 배치 변경입니다. 예를 들어 문자열 인터닝이 동작하는 방식이나 객체의 크기를 나타내는 데 쓰이는 데이터 타입은 Python 2.x를 거치면서 바뀌었습니다. 그 결과, 문자열, 리스트, 튜플의 필드에 직접 접근하는 확장 모듈은 다시 컴파일하지 않은 채 그 코드를 더 새로운 버전의 인터프리터에 로드하면 깨지게 됩니다. 다른 필드의 오프셋이 바뀌었을 수 있어서 확장 모듈이 엉뚱한 데이터에 접근하게 되기 때문입니다.

경우에 따라서는 비호환성이 프레임 객체나 코드 객체처럼 인터프리터 내부 객체에만 영향을 줍니다. 예를 들어 줄 번호를 표현하는 방식은 2.x 기간 동안 바뀌었고, (클로저의 도입으로 인해) 지역 변수를 저장하는 방식도 바뀌었습니다. 대부분의 애플리케이션은 아마 이러한 객체를 한 번도 사용하지 않았겠지만, 이들을 변경하려면 PYTHON_API_VERSION을 바꿔야 했습니다.

Linux에서는 ABI 변경이 대개 큰 문제가 되지 않습니다. 시스템이 기본 Python 설치본을 제공하고, 많은 확장 모듈이 이미 그 버전에 맞게 미리 컴파일되어 제공되기 때문입니다. 추가 모듈이나 추가 Python 버전이 필요하면 사용자는 보통 시스템에서 직접 컴파일할 수 있으며, 그 결과 올바른 ABI를 사용하는 모듈을 얻게 됩니다.

Windows에서는 여러 Python 버전을 동시에 설치하는 일이 흔하며, 확장 모듈은 최종 사용자가 아니라 작성자가 컴파일합니다. ABI 비호환성의 위험을 줄이기 위해, 현재 Python은 실제로 ABI 비호환성이 있는지와 관계없이 기능 릴리스마다 pythonXY.dll이라는 새 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에 속하지 않는 모든 정의가 숨겨집니다.

구조체

애플리케이션은 다음 구조체와 구조체 필드에만 접근할 수 있습니다:

  • PyObject (ob_refcnt, ob_type)
  • PyVarObject (ob_base, ob_size)
  • PyMethodDef (ml_name, ml_meth, ml_flags, ml_doc)
  • PyMemberDef (이름, 타입, 오프셋, 플래그, 문서)
  • PyGetSetDef (name, get, set, doc, closure)
  • PyModuleDefBase (ob_base, m_init, m_index, m_copy)
  • PyModuleDef (m_base, m_name, m_doc, m_size, m_methods, m_traverse, m_clear, m_free)
  • PyStructSequence_Field (name, doc)
  • PyStructSequence_Desc (이름, 문서, 필드, 시퀀스)
  • PyType_Slot (아래 참조)
  • PyType_Spec (아래 참조)

이 필드들에 접근하는 매크로(Py_REFCNT, Py_TYPE, Py_SIZE)도 애플리케이션에서 사용할 수 있습니다.

다음 타입들은 사용할 수 있지만, 내부가 불투명합니다(즉, 불완전한 타입입니다):

  • PyThreadState
  • PyInterpreterState
  • struct _frame
  • struct symtable
  • struct _node
  • PyWeakReference
  • PyLongObject
  • PyTypeObject

타입 객체

애플리케이션에서는 타입 객체의 구조를 사용할 수 없으므로, 더 이상 “static” 타입 객체를 선언할 수 없습니다(이 ABI를 사용하는 애플리케이션의 경우). 대신 타입 객체는 동적으로 생성됩니다. 타입을 쉽게 생성할 수 있도록(특히 함수 포인터를 쉽게 채울 수 있도록) 다음 구조체와 함수를 사용할 수 있습니다:

typedef struct{
  int slot;    /* slot id, see below */
  void *pfunc; /* function pointer */
} PyType_Slot;

typedef struct{
  const char* name;
  int basicsize;
  int itemsize;
  unsigned int flags;
  PyType_Slot *slots; /* terminated by slot==0. */
} PyType_Spec;

PyObject* PyType_FromSpec(PyType_Spec*);

슬롯을 지정하려면 고유한 슬롯 id를 제공해야 합니다. 새 Python 버전에서 새 슬롯 id가 도입될 수는 있지만, 슬롯 id가 재사용되는 일은 결코 없습니다. 슬롯이 폐지 예정(deprecated)이 될 수는 있지만, Python 3.x 전체에 걸쳐 계속 지원됩니다.

슬롯 id의 이름은 Python 3.1에서 해당 포인터를 담고 있던 구조체의 필드 이름에 Py_ 접두사를 붙인 형태입니다(즉, 그냥 tp_dealloc이 아니라 Py_tp_dealloc입니다):

  • tp_dealloc, tp_getattr, tp_setattr, tp_repr, tp_hash, tp_call, tp_str, tp_getattro, tp_setattro, tp_doc, tp_traverse, tp_clear, tp_richcompare, tp_iter, tp_iternext, tp_methods, tp_base, tp_descr_get, tp_descr_set, tp_init, tp_alloc, tp_new, tp_is_gc, tp_bases, tp_del
  • nb_add nb_subtract nb_multiply nb_remainder nb_divmod nb_power nb_negative nb_positive nb_absolute nb_bool nb_invert nb_lshift nb_rshift nb_and nb_xor nb_or nb_int nb_float nb_inplace_add nb_inplace_subtract nb_inplace_multiply nb_inplace_remainder nb_inplace_power nb_inplace_lshift nb_inplace_rshift nb_inplace_and nb_inplace_xor nb_inplace_or nb_floor_divide nb_true_divide nb_inplace_floor_divide nb_inplace_true_divide nb_index
  • sq_length sq_concat sq_repeat sq_item sq_ass_item sq_contains sq_inplace_concat sq_inplace_repeat
  • mp_length mp_subscript mp_ass_subscript

다음 필드는 타입 정의 중에 설정할 수 없습니다: - tp_dict tp_mro tp_cache tp_subclasses tp_weaklist tp_print - tp_weaklistoffset tp_dictoffset

typedef

위에 나열한 구조체용 typedef 외에도 다음 typedef를 사용할 수 있습니다. 이들이 ABI에 포함된다는 것은 한 플랫폼에서 기반 타입이 바뀌어서는 안 된다는 뜻입니다(플랫폼마다 다를 수는 있습니다).

  • Py_uintptr_t Py_intptr_t Py_ssize_t
  • unaryfunc binaryfunc ternaryfunc inquiry lenfunc ssizeargfunc ssizessizeargfunc ssizeobjargproc ssizessizeobjargproc objobjargproc objobjproc visitproc traverseproc destructor getattrfunc getattrofunc setattrfunc setattrofunc reprfunc hashfunc richcmpfunc getiterfunc iternextfunc descrgetfunc descrsetfunc initproc newfunc allocfunc
  • PyCFunction PyCFunctionWithKeywords PyNoArgsFunction PyCapsule_Destructor
  • getter setter
  • PyOS_sighandler_t
  • PyGILState_STATE
  • Py_UCS4

특히 Py_UNICODE는 typedef로 사용할 수 없습니다. 같은 Python 버전이라도 같은 플랫폼에서 Py_UNICODE를 서로 다르게 정의할 수 있기 때문입니다(좁은 코드 단위와 넓은 코드 단위 중 무엇을 사용하는지에 따라 다릅니다). 유니코드 문자열의 내용에 접근해야 하는 애플리케이션은 문자열을 wchar_t로 변환할 수 있습니다.

함수와 함수형 매크로

아래에서 제외하는 함수를 뺀 모든 함수를 기본적으로 사용할 수 있습니다. 함수가 문서화되었는지 여부는 상관없습니다.

함수형 매크로(특히 필드 접근 매크로)는 애플리케이션에서 계속 사용할 수 있지만 함수 호출로 대체됩니다(다만 여러 _Check 매크로처럼 정의가 ABI의 기능만 참조하는 경우는 예외입니다).

ABI 함수 선언의 매개변수나 반환 타입은 바뀌지 않습니다. 시그니처를 바꿔야 하면 새 함수를 도입합니다. 새 함수가 소스 호환된다면(예: 반환 타입만 바뀌는 경우) 별칭 매크로를 추가하여 애플리케이션을 다시 컴파일할 때 호출이 새 함수로 향하도록 할 수 있습니다.

기존 함수를 계속 제공할 수 없으면 해당 함수를 폐지 예정으로 표시한 뒤 제거할 수 있으며, 그러면 그 함수를 사용하는 애플리케이션이 동작하지 않게 됩니다.

제외되는 함수

_Py로 시작하는 함수는 모두 애플리케이션에서 사용할 수 없습니다. 또한 PyAST_FromNode(node*를 받습니다)처럼 애플리케이션에서 사용할 수 없는 매개변수 타입을 받는 함수도 모두 ABI에서 제외됩니다.

다음 헤더 파일에 선언된 함수는 ABI에 포함되지 않습니다:

  • bytes_methods.h
  • cellobject.h
  • classobject.h
  • code.h
  • compile.h
  • datetime.h
  • dtoa.h
  • frameobject.h
  • funcobject.h
  • genobject.h
  • longintrepr.h
  • parsetok.h
  • pyarena.h
  • pyatomic.h
  • pyctype.h
  • pydebug.h
  • pytime.h
  • symtable.h
  • token.h
  • ucnhash.h

또한 Windows에서 특정 버전의 Microsoft C 런타임 DLL에 의존하지 않도록, FILE*를 받는 함수는 ABI에 포함되지 않습니다.

모듈 및 타입의 초기화 함수와 종료 함수는 사용할 수 없습니다(PyByteArray_Init, PyOS_FiniInterrupts, 그리고 _Fini 또는 _ClearFreeList로 끝나는 모든 함수).

인터프리터 구현 세부 사항을 다루는 여러 함수는 사용할 수 없습니다:

  • PyInterpreterState_Head, PyInterpreterState_Next, PyInterpreterState_ThreadHead, PyThreadState_Next
  • Py_SubversionRevision, Py_SubversionShortBranch

PyStructSequence_InitType은 호출자가 정적 타입 객체를 제공해야 하므로 사용할 수 없습니다.

Py_FatalError는 pydebug.h에서 다른 헤더 파일(예: pyerrors.h)로 옮겨질 예정입니다.

사용 가능한 함수의 정확한 목록은 python3.dll용 Windows 모듈 정의 파일 [1]에 나와 있습니다.

전역 변수

타입과 예외를 나타내는 전역 변수는 애플리케이션에서 사용할 수 있습니다. 또한 매크로에서 참조하는 일부 전역 변수(Py_True와 Py_False 등)도 사용할 수 있습니다.

전역 변수 정의의 전체 목록은 python3.def 파일 [1]에 나와 있으며, DATA로 선언된 항목이 변수를 나타냅니다.

기타 매크로

기호 상수를 정의하는 모든 매크로는 애플리케이션에서 사용할 수 있으며, 그 숫자 값은 바뀌지 않습니다.

또한 다음 매크로도 사용할 수 있습니다:

  • Py_BEGIN_ALLOW_THREADS, Py_BLOCK_THREADS, Py_UNBLOCK_THREADS, Py_END_ALLOW_THREADS

버퍼 인터페이스

버퍼 인터페이스(Py_buffer 타입, bf_getbuffer 및 bf_releasebuffer 타입 슬롯 등)는 현재로서는 Py_buffer 구조체의 안정성이 명확하지 않으므로 ABI에서 제외되었습니다. 향후 릴리스에서 ABI에 포함하는 것을 검토할 수 있습니다.

시그니처 변경

호출자는 보통 PyObject*를 가지고 있는데도, 현재 여러 함수가 특정 구조체를 인자로 요구합니다. 이 함수들은 PyObject*를 매개변수로 받도록 변경되었습니다. 이로 인해 현재 매개변수 타입으로 명시적으로 캐스트하는 애플리케이션에서는 경고가 발생합니다. 해당 함수는 PySlice_GetIndices, PySlice_GetIndicesEx, PyUnicode_AsWideChar, PyEval_EvalCode입니다.

링크

Windows에서 애플리케이션은 python3.dll과 링크해야 하며, 임포트 라이브러리인 python3.lib도 제공됩니다. 이 DLL은 /export 링커 옵션을 통해 자신의 모든 API 함수를 전체 인터프리터 DLL, 즉 python3y.dll로 리디렉션합니다.

Unix 시스템에서 ABI는 일반적으로 python 실행 파일 자체가 제공합니다. 확장 모듈이 Py_LIMITED_API로 컴파일된 경우 API 버전으로 3을 전달하도록 PyModule_Create가 변경됩니다. 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].

참고 문헌