파이썬 패키징의 시작점 - setuptools#

파이썬 모듈 배포는 다음과 같은 방법이 주로 사용됩니다.

  1. 패키지 파일로 묶어서 배포

  2. 압축 파일로 묶어서 배포

이 글에서는 패키지 파일로 묶어서 배포하는 방법에 오랫동안 사용되어온 setuptools 패키지를 살펴보겠습니다. setuptools가 하는 일은 파이썬 소스 코드, 관련 리소스 파일을 패키징 파일 및 압축 파일로 만들어줍니다.

파이썬 스크립트를 어떻게 배포할 수 있지?#

초기의 파이썬 설계에서는 모듈을 배포하는 일이 중요하게 여겨지지 않았지만 파이썬 모듈 배포 요구가 생기면서 파이썬 핵심 개발팀은 1998년 말에 distutils-sig 그룹을 만들어 배포 관련 기능을 논의하기 시작합니다. 이 논의의 결과물인 distutils가 개발되어 1.5.2 버전 때는 외부 패키지로 2000년에 파이썬 1.6에 공식 라이브러리로 포함되어 배포됩니다.

distutils가 개발되던 때에도 다른 파이썬 패키징 도구가 고유한 규격을 가지고 개발되긴 했지만 distutils가 파이썬 패키징 표준으로 인정받게 된 것은 다른 패키징 도구에 없던 C 확장 라이브러리를 컴파일하는 기능이 포함되어 있었기 때문이었습니다. distutils를 사용한 모듈 배포에는 다음 2개의 파일이 필요합니다.

  • setup.py

  • requirements.txt

이 2개의 파일의 역할은 다음과 같습니다. setup.py 는 파이썬 소스 코드와 파이썬 패키지 디렉터리들을 패키징 파일로 만드는데 사용하고 requirements.txt 는 배포된 파이썬 모듈을 사용하는데 필요한 외부 패키지 이름과 버전을 명시하는데 사용합니다.

setup.py 은 파이썬 코드로 작성하며 distutils에서 가져온 setup 함수에 만들려고 하는 파이썬 패키지 정보를 함수 인자로 전달합니다.

그리고 다음과 같은 명령어로 파이썬 바이너리 패키지를 만듭니다.

$ python setup.py bdist

이렇게 하면 .tar.gz 형식으로 배포 파일이 만들어집니다. distutils로 만들어진 모듈 파일을 배포하려면 .tar.gz 파일과 requirements.txt를 함께 배포하면 되었습니다.

distutils 구조 설명#

distutils로 파이썬 모듈들을 배포하려면 다음과 같이 setup.py를 작성해야 합니다.

from distutils.core import setup

setup(name='foo',
      version='1.0',
      py_modules=['foo']
     )

setup 함수에는 여러 인자를 전달할 수 있지만 예시에서는 name, version, py_modules 인자를 전달했습니다. name은 패키지의 이름, version은 패키지의 버전을 문자열로 지정합니다. 마지막으로 py_modules 인자에는 list 타입 인자를 전달했습니다.

py_modules 인자에 전달하는 리스트의 요소값은 패키지에 포함하려는 모듈을 명시합니다. 이 때 모듈은 파이썬 파일을 의미하며 여기에는 모듈의 이름만 명시하면 되기 때문에 .py 확장자는 빼고 명시합니다.

만약 패키지에 포함하려는 파이썬 모듈이 setup.py 와 같은 레벨에 있으면 앞의 코드를 사용하고 하위 패키지에 있으면 package_dir 인자와 packages 인자를 전달합니다.

이렇게 setup.py 를 만들었으면 python 명령으로 패키징을 합니다.

$ python setup.py bdist

하지만 distutils에는 여러 문제가 있었습니다. 대표적으로 파이썬 코드를 패키징할 순 있었지만 종속성 패키지를 requirements.txt로 따로 관리해야 하며 메타 파일은 패키지에 포함할 수 없었습니다.

setuptools의 등장#

distutils는 파이썬 모듈을 쉽게 배포할 수 있도록 도와주긴 했지만 얼마 지나지 않아 distutils가 가진 문제에 부딪치면서 새로운 배포 도구 필요성이 증대되었습니다. 이에 Phillip Eby 가 distutils를 확장한 setuptools 0.3a1을 2004년에 발표했습니다.

setuptools는 distutils의 구조와 기능을 가져왔기 때문에 distutils에서 작성했던 setup.py 는 아래와 같이 바꿔쓸 수 있습니다.

from setuptools import setup

setup(name='foo',
      version='1.0',
      py_modules=['foo']
     )

setup 함수를 가져오는 위치를 제외하고는 distutils와 setuptools 모든게 동일한 것을 볼 수 있습니다. 개별 모듈이 아니라 패키지를 사용할 때도 같습니다. 패키징 파일을 만드는 것도 distutils를 사용할 때와 같습니다.

$ python setup.py bdist

distutils에 비해 진보적이고 패키지 설치 시 종속성 관리, 메타 데이터 등을 포함할 수 있는 기능을 가지고 있었기에 많은 사람들의 지지를 받았고 2004년부터 파이썬 재단은 distutils 대신에 setuptools를 쓰는 것을 권장하기 시작했습니다.

setuptools는 파이썬 2.4와 3.4 이전에는 따로 설치해서 사용할 수 밖에 없었지만 파이썬 2.4와 3.4가 출시되면서 setuptools가 설치되기 시작했습니다.

egg의 등장#

setuptools는 0.3a2와 함께 egg 라는 독자 패키지 파일 포맷과 이 패키지 파일을 파이썬 환경에 설치할 수 있도록 하는 easy_install 프로그램을 제공하기 시작했습니다.

distutils를 사용해 만들어진 패키지 파일 배포는 여러 문제가 있었습니다. 그 중 하나가 패키지가 C 확장을 포함하고 있는 경우입니다. 이 경우 C 확장이 어떤 파이썬 버전에 어떤 운영체제에서 동작하는지 여부 등을 알 수 없기도 했고 설치된 파이썬 패키지가 어떤 파일들을 포함하고 있는지 정보를 전혀 알 수 없다는 것이었습니다.

이런 문제를 해결하기 위해 파이썬 커뮤니티에서 제안된 것이 egg 포맷입니다. egg 포맷은 패키지의 메타 정보와 패키지 소스 파일, 컴파일된 C 확장 등을 포함하는 zip 압축 파일입니다. 다른 언어에서 개념을 빌려온다면 .jar 와 비슷한 구조입니다.

egg 포맷은 C 확장을 포함한 모듈들이 독자적으로 묶여있기 때문에 배포하기도 쉽고 사용하기도 쉬웠습니다. 자바의 jar를 사용할 때처럼 PYTHONPATH 환경 변수에 egg 파일의 경로만 담아두어도 파이썬 인터프리터에서 사용할 수 있었을 정도였습니다.

egg 포맷은 패키지를 설명하기 위해 패키지의 메타 데이터를 저장하기 .egg-info 이름을 가진 디렉터리를 만듭니다. 이 디렉터리에는 패키지를 설명하기 위한 메타 데이터를 .txt 파일 형식으로 관리합니다.

대표적으로 다음과 같은 메타 데이터 파일이 .egg-info 디렉터리에 만들어집니다. 패키지 설정에 따라 파일들이 만들어지지 않을 수도 있습니다. [1]

  • top_level.txt

  • SOURCES.txt

  • requires.txt

  • setup_requires.txt

  • dependency_links.txt

  • namespace_packages.txt

  • entry_point.txt

  • PKG-INFO

egg 파일은 다음과 방법으로 생성합니다.

$ python setup.py bdist_egg

이 명령을 통해 egg 파일이 만들어지면 easy_install 명령을 사용해 egg 파일을 파이썬 환경에 설치할 수 있습니다.

$ easy_install sample.egg

easy_install은 egg 파일 뿐만 아니라 .tar.gz 로 묶인 패키지 파일 설치도 가능합니다. easy_install 이전엔 파이썬 패키지를 설치하려면 패키지 파일의 압축을 풀고 setup.py 스크립트를 직접 실행했어야 했습니다.

egg 와 easy_install의 규격은 파이썬 표준안인 PEP로 받아들여지진 않았지만 몇 년 동안 파이썬 중앙 패키지 저장소인 PyPI 및 SVN 저장소로부터 패키지를 설치하는데 유용하게 사용되었습니다.

하지만 easy_install 은 패키지 설치에만 최적화 되어 있을 뿐 이미 설치된 패키지를 제거하지 못하는 문제가 있었습니다.

이 글의 뒤에서 소개하지만 egg 포맷의 대체인 wheel 포맷을 사용하는 현재 .egg-info 디렉터리는 현재도 사용되고 있습니다.

pip의 등장#

파이썬 가상 환경 패키지인 virtualenv를 개발한 Ian Bicking은 easy_install에 있었던 여러 문제를 해결한 도구인 pip를 2008년에 공개했습니다. pip(package installer for Python)는 파이썬 패키지를 설치하고 삭제하는 기능 외에도 파이썬 환경에 설치된 패키지 목록 등을 볼 수 있는 기능을 제공했습니다.

pip가 easy_install에 비해서 좋은 도구였기에 setuptools 때와 같이 곧 많은 사람들의 지지를 얻었습니다. 이 지지를 발판 삼아 pip는 Python 2.4와 Python 3.4에 포함되어 배포되기 시작했습니다.

pip 1.1 버전과 현재의 pip는 기능의 차이가 있지만 install 기능과 uninstall 기능이 들어온 것만으로도 pip는 easy_install 과 비교해 단점들이 채워졌습니다.

pip 이전에 setup.py를 사용해 파이썬 환경에 개발중인 프로그램을 편집 모드로 설치하려면 python setup.py develop 명령을 실행했었습니다. pip가 나온 뒤에는 pip install -e . 명령을 사용하기 때문에 만약 파이썬 패키징 도구로 setuptools 가 사용되지 않더라도 상관없습니다.

pypa의 등장#

setuptools, virtualenv, pip 의 등장은 파이썬 생태계에 있어 매우 놀라운 변화를 가져왔지만 어디까지나 개인이 개발한 도구였던 탓에 유지 보수 등의 문제가 있었습니다. 이에 따라 Ian Bicking 이 개발하고 led by Carl Meyer, Brian Rosner and Jannis Leidel 이 유지보수한 virtualenv, pip 를 이어받기 위한 논의가 촉발됐습니다.

이 논의의 결과로 2011년 2월 18일에 PyPA가 출범하게 됩니다. PyPA가 출범하게 되면서 파이썬 핵심 개발팀은 패키징에 관한 기능은 PyPA에 미루고 핵심 기능 개발에 매진하게 됩니다. 2024년 현재 PyPA는 파이썬 패키징과 관련해 가장 권위있고 패키징 표준에 활발하게 참여하는 단체입니다.

wheel 포맷의 등장#

PyPA 출범 이후 파이썬 패키징 생태계는 .egg 포맷이 가지고 있던 여러 단점을 보완하기 위해 새로운 바이너리 포맷을 만들게 됩니다. Distutils-SIG 그룹은 새로운 바이너리 포맷을 위한 태그 호환성 기준을 2012년 9월 8일에 PEP 425 로 발표하고 2012년 9월 20일에 PEP 427 을 발표함으로서 egg 포맷을 뒤로 하게 됩니다.

wheel 바이너리 포맷은 wheel 이라는 패키지를 설치해야만 사용할 수 있지만 wheel 패키지는 setuptools를 사용해 만들 수 있습니다. setup.py를 사용한 경우 다음 명령어로 만듭니다.

$ python setup.py bdist_wheel

wheel 포맷 이전에 가장 많이 쓰였던 패키징 포맷이 egg 포맷이었기 때문에 프로그램을 pip 명령을 사용해 파이썬 환경에 설치하면 .egg-info 디렉터리가 생성되는 등 아직 egg 포맷의 흔적이 남아있습니다.

egg 포맷은 .egg 파일에 포함되었던 .egg-info 디렉터리를 포함하지만 wheel 포맷은 .dist-info 디렉터리를 포함하도록 변경되었습니다.

마치며..#

이 글에서는 파이썬 패키징 생태계에서 가장 오랫동안 사용되어온 setuptools와 관련 생태계를 살펴보았습니다. 과거와 달리 최근의 파이썬 패키징은 setuptools에만 의존하고 있지 않습니다.

파이썬 패키징 도구는 과거와 달리 더 이상 독점적이지 않은데다 setuptools 자체가 파이썬의 표준 패키징 도구는 아닙니다. 이에 더해 파이썬은 setuptools 외에도 여러 빌드 도구를 사용하는 것을 권장하고 있습니다.

필자가 알고 있는 빌드 도구로는 flit, poetry, pdm 등이 있습니다. 이들 도구에 대해선 다음에 설명할 기회가 있을것으로 생각합니다.

이번 글은 여기에서 마치겠습니다.

from. 파이썬 수다장이 날다의 아저씨

각주

Comments

comments powered by Disqus