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.py와 two.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).

PEP 384가 안정 ABI를 정의하기는 하지만, 이는 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처럼 이름이 붙기 때문에, foo가 둘 이상의 Python 버전용으로 제공되면 이 파일들 역시 이름이 충돌합니다.

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

PyPy [5]도 이 PEP의 혜택을 받아, 자체 API용으로 빌드된 확장 모듈에 다른 .so 태그를 붙임으로써 이름 충돌을 피할 수 있습니다.

제안

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

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

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

이 두 필드는 하이픈으로 구분하며, 메이저 버전 번호와 마이너 버전 번호 사이에는 점을 넣지 않습니다. 예: cpython-32.

Python 구현은 필요에 따라 파일 이름 태그에 추가 플래그를 포함할 수 있습니다(MAY). 예를 들어, POSIX 시스템에서는 다음 플래그들도 파일 이름에 반영됩니다:

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

파이썬 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 파일 이름으로 대체해야 합니다.

검증된 접근 방식

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

Windows

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

PEP 384

PEP 384는 확장 모듈을 위한 안정 ABI를 정의합니다. 이론적으로는 PEP 384가 보편적으로 채택되면 모든 확장 모듈이 어떤 Python 버전과도 호환될 수 있으므로 이 PEP는 필요 없게 됩니다. 물론 현실적으로는 보편적인 채택을 이루는 것이 불가능할 것이며, 앞서 설명했듯이 서로 다른 빌드 시점 플래그는 여전히 ABI에 영향을 줍니다. 따라서 안정 ABI가 있더라도 이 PEP는 여전히 필요할 수 있습니다. 완전한 명세는 PEP 384에 맡기고, 여기서는 관련 문제들을 논의합니다.

PEP 384는 확장을 Py_LIMITED_API로 컴파일한 경우 API 버전으로 3을 전달하도록 PyModule_Create()를 변경하는 내용을 설명합니다. 이 값은 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는 노출된 구조체인 PyObject의 레이아웃을 변경하므로 안정 ABI에서 지원되지 않습니다.
  • --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], 새 변경 사항이 업로드될 때마다 자동으로 갱신됩니다.

참고 문헌