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

Python 개선 제안 한국어 번역

PEP 3149 – ABI 버전 태그가 붙은 .so 파일

Author:
Barry Warsaw <barry at python.org>
Status:
Final
Type:
Standards Track
Created:
09-Jul-2010
Python-Version:
3.2
Post-History:
14-Jul-2010, 22-Jul-2010
Resolution:
Python-Dev message

Table of Contents

번역·라이선스 안내

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

초록

PEP 3147은 각 소스 파일과 함께 둘 이상의 바이트 컴파일 파일(.pyc)을 배치할 수 있도록 하여 Python 소스 코드의 공유를 개선하는 Python의 임포트 메커니즘 확장을 설명합니다.

이 PEP는 확장 모듈 파일(.so)을 유사한 방식으로 함께 배치할 수 있도록 하는 보조 기능을 정의합니다. 이 선택적 빌드 시 기능을 통해 Python의 다운스트림 배포판은 둘 이상의 Python 주 버전을 동시에 더 쉽게 제공할 수 있습니다.

배경

PEP 3147은 시스템에서 여러 Python 버전을 사용할 수 있을 때 순수 Python 패키지의 파일 시스템 배치를 정의합니다. 예를 들어 소스 모듈 one.pytwo.py를 포함하는 alpha 패키지가 Python 3.2 및 3.3이 설치된 시스템에 존재하는 경우, 바이트 컴파일 후의 파일 시스템 배치는 다음과 같습니다.:

alpha/
    __pycache__/
        __init__.cpython-32.pyc
        __init__.cpython-33.pyc
        one.cpython-32.pyc
        one.cpython-33.pyc
        two.cpython-32.pyc
        two.cpython-33.pyc
    __init__.py
    one.py
    two.py

확장 모듈이 있는 패키지의 경우 모듈의 .so 파일에도 이와 유사한 구분이 필요합니다. 서로 다른 Python 주 버전에 맞게 컴파일된 확장 모듈은 ABI의 변경으로 인해 서로 호환되지 않습니다. 동일한 Python 버전에 대해 서로 다른 구성/컴파일 옵션을 사용하면 서로 다른 ABI가 생성될 수 있습니다(예: –with-wide-unicode).

안정적인 ABI를 정의하는 PEP 384는 Python 빌드 또는 주요 버전 간 확장 모듈 비호환성을 최소화하지만 제거하지는 않습니다. 따라서 확장 모듈 파일 이름을 구별하기 위한 메커니즘이 제안됩니다.

근거

Ubuntu [3]와 Debian [4]같은 Linux 배포판은 사용자에게 둘 이상의 Python 버전을 동시에 제공합니다. 예를 들어 Ubuntu 9.10 Karmic Koala 사용자는 Python 2.5, 2.6 및 3.1을 설치할 수 있으며, 기본 버전은 Python 2.6입니다.

사용 가능한 Python 버전 간에 가능한 한 많이 공유하기 위해 이러한 배포판은 서드파티 패키지 모듈(.pyc.so 파일)을 /usr/share/pyshared에 설치하고, /usr/lib/pythonX.Y/dist-packages에서 해당 위치로 심볼릭 링크를 생성합니다. 이러한 심볼릭 링크가 존재하는 이유는 PEP 3147 이전의 환경(즉, Python 3.2 미만)에서는 설치된 여러 Python이 바이트 컴파일하여 생성한 .pyc 파일의 이름이 서로 충돌하기 때문입니다. Python 버전이 >= 3.2인 경우 .pyc 파일이 더 이상 파일 시스템 이름 충돌을 일으키지 않으므로 모든 순수 Python 패키지를 공유할 수 있습니다. 이러한 심볼릭 링크를 제거하면 Python 배포판이 더 단순하고 견고해집니다.

공유 라이브러리 확장에서도 이와 유사한 상황이 발생합니다. 확장 모듈의 이름은 일반적으로 foo 확장 모듈에 대해 foo.so로 지정되므로, 둘 이상의 Python 버전에 foo가 제공되면 이 이름도 충돌합니다.

또한 동일한 Python 버전에 대해 서로 다른 구성/컴파일 옵션을 사용하면 확장 모듈에 서로 다른 ABI가 제공될 수 있기 때문입니다. 예를 들어 POSIX 시스템에서는 --with-pydebug, --with-pymalloc--with-wide-unicode 구성 옵션이 모두 ABI를 변경합니다. 이 PEP는 빌드 시 옵션을 .so 확장 모듈 파일의 이름에 인코딩할 것을 제안합니다.

PyPy [5]도 이 PEP의 혜택을 받을 수 있으며, 서로 다른 .so 태그를 사용하여 해당 API용으로 빌드된 확장 모듈의 이름 충돌을 피할 수 있습니다.

제안

Python 인터프리터의 빌드 시 선택된 구성/컴파일 옵션은 확장 모듈의 공유 라이브러리 파일 이름에 인코딩됩니다. 이 “태그”는 모듈 기본 이름과 공유 라이브러리의 운영 체제 파일 시스템 확장자 사이에 표시됩니다.

공유 라이브러리 파일 이름에는 다음 정보가 MUST 포함되어야 합니다.

  • Python 구현(예: cpython, pypy, jython 등)
  • 인터프리터의 주 버전 및 부 버전 번호

이 두 필드는 하이픈으로 구분되며 주 버전 번호와 부 버전 번호 사이에는 점이 나타나지 않습니다. 예: cpython-32.

Python 구현은 적절한 경우 파일 이름 태그에 추가 플래그를 MAY 포함할 수 있습니다. 예를 들어 POSIX 시스템에서는 이러한 플래그도 파일 이름에 반영됩니다:

  • --with-pydebug (플래그: d)
  • --with-pymalloc (플래그: m)
  • --with-wide-unicode (플래그: u)

Python 3.2에서는 기본적으로 configure--with-pymalloc을 활성화하므로 공유 라이브러리 파일 이름은 foo.cpython-32m.so로 표시됩니다. 나머지 두 플래그도 활성화하면 파일 이름은 foo.cpython-32dmu.so가 됩니다.

공유 라이브러리 파일 이름 태그는 무조건 사용되며 변경할 수 없습니다. 태그와 확장 모듈 접미사는 다음 변수를 통해 sysconfig 모듈에서 사용할 수 있습니다:

>>> sysconfig.get_config_var('EXT_SUFFIX')
'.cpython-32mu.so'
>>> sysconfig.get_config_var('SOABI')
'cpython-32mu'

$SOABI는 태그만 포함하는 반면, $EXT_SUFFIX는 공유 라이브러리 파일의 플랫폼 확장자를 포함하며 확장 모듈 이름에 추가되는 정확한 접미사입니다.

임의의 패키지 foo에 대해 배포 패키지가 설치되었을 때 다음 파일을 볼 수 있습니다:

/usr/lib/python/foo.cpython-32m.so
/usr/lib/python/foo.cpython-33m.so

(이 경로는 예시일 뿐입니다. 배포판은 원하는 파일 시스템 레이아웃을 자유롭게 사용할 수 있으며, 이 PEP의 어떤 내용도 소스에서 빌드한 Python이 설치되는 위치를 변경하지 않습니다.)

Python의 동적 모듈 로더는 빌드 시 옵션과 일치하는 태그가 있는 공유 라이브러리 확장 모듈을 인식하고 가져옵니다. 하위 호환성을 위해 Python은 태그가 없는 확장 모듈도 계속 가져옵니다. 예: foo.so.

이 공유 라이브러리 태그는 파일 시스템의 어디에서 빌드되었는지와 관계없이 모든 distutils 기반 확장 모듈에 전역적으로 사용됩니다. distutils 이외의 방법으로 빌드한 확장 모듈은 태그를 수동으로 계산하거나 태그가 없는 .so 파일 이름으로 대체해야 합니다.

검증된 접근 방식

여기서 설명하는 접근 방식은 어떤 의미에서는 Debian 및 Ubuntu 시스템에서 이미 검증되었습니다. 이러한 시스템에서는 Python과 확장 모듈의 디버그 빌드에 서로 다른 확장자가 사용됩니다. Windows의 디버그 빌드도 동적 라이브러리에 이미 다른 파일 확장자를 사용하며, 실제로 (이 PEP에서 제안하는 방식과는 다르게) .dll 파일 이름에 Python의 주 버전과 부 버전을 인코딩했습니다.

Windows

이 PEP는 configure 스크립트를 사용하는 POSIX 시스템의 빌드 문제만 다룹니다. Windows 또는 다른 플랫폼에 대한 지원이 이 PEP에서 명시적으로 금지되는 것은 아니지만, 그러한 플랫폼에서의 지원을 평가하고 설명하며 구현하려면 플랫폼 전문 지식이 필요합니다. 현재로서는 이 PEP의 기능이 Windows에 유용한지조차 분명하지 않습니다.

PEP 384

PEP 384는 확장 모듈을 위한 안정적인 ABI를 정의합니다. 이론적으로 PEP 384를 보편적으로 채택하면 모든 확장 모듈이 모든 Python 버전과 호환될 수 있으므로 이 PEP의 필요성이 사라집니다. 물론 실제로는 보편적인 채택을 달성할 수 없으며, 위에서 설명한 것처럼 서로 다른 빌드 시 플래그가 여전히 ABI에 영향을 미칩니다. 따라서 안정적인 ABI가 있더라도 이 PEP는 여전히 필요할 수 있습니다. 완전한 사양은 PEP 384에 맡기고, 여기서는 관련 문제를 논의합니다.

PEP 384PyModule_Create()의 변경 사항을 설명합니다. 확장 모듈이 Py_LIMITED_API로 컴파일된 경우 API 버전으로 3이 전달됩니다. 이는 PYTHON_API_VERSION에 맞춰 PYTHON_ABI_VERSION이라는 공식 매크로로 공식화해야 합니다. ABI가 호환되지 않는 방식으로 변경되는 경우 이 버전 번호를 증가시킵니다. 공유를 용이하게 하기 위해 Python이 이름에 PYTHON_ABI_VERSION 번호가 포함된 확장 모듈을 검색하도록 확장합니다. abi 접두사는 Python에서 사용하도록 예약됩니다.

따라서 Python이 기본 플래그 집합으로 구성된 경우, PEP 384의 초기 구현은 확장 모듈 foo를 가져올 때 다음 파일 이름을 이 순서로 검색합니다.:

foo.cpython-XYm.so
foo.abi3.so
foo.so

모듈 작성자가 자신의 확장 기능이 해당 버전의 ABI를 지원한다고 나타내면 distutils [6]build_ext 명령도 abi3 태그가 붙은 공유 라이브러리 파일로 컴파일하도록 확장해야 합니다. 이는 다음과 같이 Extension 클래스에 키워드 인자를 추가하여 하위 호환성을 유지하는 방식으로 수행할 수 있습니다.:

Extension('foo', ['foo.c'], abi=3)

Martin v. Löwis는 이 PEP를 PEP 384에 적용할 수 있는지에 대한 자신의 견해를 설명합니다 [7]. 요약하면 다음과 같습니다.

  • --with-pydebug는 안정 ABI에서 지원되지 않습니다. 이는 노출된 구조체인 PyObject의 레이아웃을 변경하기 때문입니다.
  • --with-pymalloc은 이 문제와 관련이 없습니다.
  • --with-wide-unicode는 더 복잡하지만, Martin은 안정 ABI가 플랫폼의 wchar_t와 일치하는 Py_UNICODE를 사용하도록 강제하는 쪽으로 기울고 있습니다.

대안

이 아이디어가 처음 소개된 초기 python-dev 스레드 [8]에서 여러 대안이 제안되었습니다. 완전성을 위해 이를 채택하지 않은 이유와 함께 여기에 나열합니다.

독립 디렉터리 또는 심볼릭 링크

Debian과 Ubuntu는 해당 Python 버전의 확장 모듈만 포함하는 버전별 디렉터리를 sys.path에 추가할 수 있습니다. 또는 PEP 3147에서 제거된 심볼릭 링크 방식을 공유 라이브러리에만 유지할 수도 있습니다. 이 접근 방식은 PEP 3147이 피하려는 본질적인 복잡성을 그대로 확산시키며, 확장 모듈 수가 전체 Python 패키지 수보다 훨씬 적은 경우에도 모든 모듈을 검색할 디렉터리를 잠재적으로 여러 개 추가하므로 거부됩니다. 예를 들어 와이드 유니코드 사용 여부, pydebug 사용 여부, pymalloc 사용 여부에 따라 빌드를 각각 제공하면 검색할 디렉터리의 총수가 크게 증가합니다.

확장 모듈이 포함된 패키지를 공유하지 않기

배포판에서 확장 모듈이 포함된 Python 패키지를 지원되는 모든 Python 버전 간에 공유하지 말자는 제안이 있었습니다. 관련 PEP 3149를 채택하더라도 확장 모듈은 지원되는 모든 Python 버전별로 컴파일해야 하므로, 어쩌면 그러한 패키지를 공유하는 것은 어차피 유용하지 않을 수 있습니다. 그러나 확장 기능이 포함된 패키지를 공유하지 않는 것은 여러 가지 이유로 실행 불가능합니다.

한 버전에서 순수 Python 패키지를 공유하고 있다면, 다음 릴리스에서 속도 향상을 위한 확장 모듈이 추가되었다는 이유만으로 갑자기 공유하지 않아야 합니까? 또한 모든 확장 공유 라이브러리를 지원되는 각 Python 버전용으로 한 번씩 컴파일하고 배포하더라도, 모든 .so 파일을 중복하는 것과 모든 .py 파일을 중복하는 것 사이에는 큰 차이가 있습니다. 추가된 크기는 이러한 패키지의 다운로드 시간을 늘리며, 더 직접적으로는 이미 제한된 배포 CD-ROM의 공간 부담을 증가시킵니다.

참조 구현

이 코드에 대한 작업은 Python 3.2에 병합할 준비가 될 때까지 Launchpad [9]의 Bazaar 브랜치에서 추적됩니다. 작업 중인 diff는 [10]에서도 확인할 수 있으며, 새로운 변경 사항이 업로드될 때마다 자동으로 업데이트됩니다.

참고 자료