Python Module 의 배포를 위해서는 일반적으로 Python Distribution Utilities ("Distutils") 라는 모듈을 사용하게 된다. 작성한 Python 모듈을 패키지할 필요성이 생겨서 이에 대해서 살펴보면서 정리를 한다.
Distutils 를 사용하는 것은 모듈 개발자와 3rd 파티 모듈을 설치하려는 사용자/관리자 모두에게 매우 간단할 일이다. 개발자로서는
의 일을 갖게 된다.
이러한 태스크들의 각각에 대해서 본 문서에서 설명하게 된다.
1.2 Simple Example
setup 스크립트는 일반적으로 매우 간단하다. 특히 python 으로 짜여있기 때문에, 특별한 제약을 갖지 않는다.
Autoconf 스타일의 설정 스크립트와는 달리, setup 스크립느는 개발한 모듈의 배포판을 빌딩 그리고 설치하는 데 있어서
여러번 수행될 수도 있다.
만약 하나의 foo.py 파일을 갖고 있는 foo 라 불리오는 모듈을 배포하기 위해서는 다음과 같이 setup 스크립트를 작성한다.
from distutils.core import setup
setup (name='foo',
version='1.0',
py_modules=['foo'],
)
살펴봐야 할 점은
- distutils 에 제공하는 대부분의 정보는 setup 함수에 keyword 아규먼트들로 제공된다.
- 이러한 키워드 아규먼트들은 두개의 카테고리들로 나뉜다: package metadata (이름, 버전 번호) 그리고 패키지 안에 무엇이 있는지에 관한 정보 (순수한 파이선 모듈들의 목록)
- 모듈들은 파일의 이름이 아닌 모듈 이름에 의해서 한정된다.
- 가급적 많은 메타데이터를 제공하는 것을 추천하는데, 특별히 예를 들자면 개발자 이름, Email 주소, 그리고 프로젝트의 URL 등을 들 수 있다.
이 모듈의 소스 배포판을 생성하기 위해서, 위 코드를 담고 있는 setup 스크립트 setup.py 를 생성한후, 수행한다:
python setup.py sdist
이는 윈도에서는 Zip, 유닉스에서는 tarball 형태로 archive 파일을 만드는데, 이는 setup.py 와 모듈
foo.py 를 담게 된다. archive 파일은 foo-1.0.tar.gz (혹은 .zip)이 될 것이고, foo-1.0
디렉토리에 풀리게 될 것이다.
만약 사용자가 foo 모듈을 설치하기를 원한다면, 사용자가 해야할 일은 foo-1.0.tar.gz 를 다움 받아서 이의 압축을 풀고, foo-1.0 디렉토리에 들어가서
python setup.py install
을 수행하는 것이다. 이는 궁극적으로는 사용자가 파이선을 설치한 곳의 3rd 파티 모듈을 위한 적절한 디렉토리에 foo.py 를 카피하게 된다.
이러한 간단한 예제는 Distutils 의 일부 기본적인 개념을 보여준다. 첫째, 개발자와 설치가 둘다 같은 기본 사용자
인터페이스를 갖는다. 차이라고는 단지 Distutils를 이용할 때 sdist 커맨트를 사용하느냐와 install 을
사용하느냐의 차이이다.
만약 개발자가 사용자를 위해서 정말 뭔가 더 쉽게 만들어주고 싶다면, 하나 혹은 그 이상의 내장된 배포판을 만들 수 있다.
예를 들어서 개발자가 위도 머신을 사용하고 있을 때, 다른 윈도 사용자를 위해서 더 쉽게 만들어주고 싶다면, 개발자는
bdist_wininst 커맨드를 이용해서 다음과 같이 수행가능한 설치 파일을 만들어줄 수 있다.
python setup.py bdist_wininst
위 커맨드는 수행가능한 인스톨러인 foo-1.0.win32.exe 파일을 생성하게 된다.
다른 유용한 내장된 배포 포맷은 RPM 인데 이는 bdist_rpm 커맨드를 이용하면 된다. 솔라리스의 pkgtool 은 (bdist_pkgtool), HP-UX swinstall (bdist_sdux)를 이용하면 된다.
다음과 같은 커맨드를 수행해서 어떠한 배포 포맷이 가능하지를 찾아낼 수도 있다.
python setup.py bdist --help-formats
1.3 General Python terminology
module
Python 에서 코드 재사용의 기본 유닛이 된다. 여기에는 pure Python 모듈, 확장 모듈, 그리고 패키지 세가지 형태가 존재한다.
pure Python 모듈
Python 으로 쓰여진 모듈. 하나의 .py 파일에 포함되고 종종 "pure module"이라 불리운다.
extension 모듈
Python 구현의 저수준 언어인 C/C++ for Python, Java for Jython 등으로 작성된 모듈.
기본적으로 하나의 동적으로 로딩이 가능한 pre-compiled 파일 (예를 들자면 유닉스에서는 Python 확장을 위한
shared object (.so) 파일, 윈도에서는 .pyd 확장자를 갖는 DLL 파일 Jython 확장을 위한 Hava
클래스 파일)
package
다른 모듈을 포함하고 있는 하나의 모듈; 기본적으로 filesystem 에서 하나의 디렉토리를 포함하고 다른 디렉토리와는 __init__.py 파일로 구분된다.
root package
package들의 계층에있어서 root. (이는 __init__.py 를 갖지 않기에 진짜 패키지는 아니지만 일반적으로 그렇게 부르곤 한다.)
1.4 Distutils-specific terminology
다음 용어들은 Distutils를 이용해서 배포되는 Python 모듈들에 대해서 적용된다.
module distribution
하나의 다운로드가 가능한 자원으로서 같이 배포되는 파이션 모듈들의 집합으로 한꺼번에 설치되는 것을 의미. 일부 잘 알려진 모듈 배포판의 예로는 Numeric Python, PyXML, PIL 혹은 mxBase가 있다.
pure module distribution
오직 pure Python 모듈들과 패키지들을 담고 있는 하나의 모듈 배포판. 종종 pure distribution 이라고도 불림
non-pure module distribution
적어도 하나의 extension 모듈을 담고 있는 모둘 배포판. 종종 non-pure distribution 이라고도 불림
distribution root
개발한 소스트리의 최상의 디렉토리; 디렉토리는 setup.py 가 존재하고 일반적으로 해당디렉토리에서 setup.py 가 수행된다.
2. Writing the Setup Script
setup script 는 Distutils 를 이용해서 모듈을 생성, 배포, 설치하는 데 있어서 모든 행동의 중심이
된다. setup script의 주 목표는 개발한 모듈 배포판을 Distutils에게 기술하기 위함히고, 그래서 개발한 모듈에
대해서 적절한 일을 하는 다양한 커맨드들을 갖고 있다. 1.2 에서 기술된 바와 같이, setup script 는 주로
setup() 을 호출하는 것으로 구성되고, 모듈 개발자에 의해서 Distutils에 제공되는 대부분의 정보는 keyword
arguments 로 setup() 에 제공된다.
다음은 다음 두 섹션에서 살펴보게된 간단한 예제이다.
#!/usr/bin/env python
from distutils.core import setup
setup(name='Distutils',
version='1.0',
description='PythonDistribution Utilities',
author='Greg Ward',
author_email='gward@python.net',
url='http://www.python.org/sigs/distutils-sig/',
packages=['distutils', 'distutils.command'],
)
여기에서 제공된 예제는 1.2 에서 제공된 예제와 두가지 차이점을 가지고 있다. 더많은 메타데이터가 사용되었고, 모듈보다는
패키지에 의해서 pure Python 모듈들이 기술되었다. 이러한 부분은 중용한 부분으로 Distutils 가 두개의 패키지들에
여러모듈로 구성되어 있다. 모든 모듈들의 목록을 유지하기란 많들기도, 유지하기도 힘들 일이다. 더 자세한 내용을 원한다면 2.8
장의 추가적인 메타 데이터를 보기 바란다.
setup script 에 제공되는 어떠한 패스이름들 (파일이름 혹은 디렉토리 이름)은 Unix 컨벤션을 이용해서
작성되어야 한다. Distutils 는 실제로 사용하기 전에 이러한 플랫폼 중립적 표현을 당신이 사용하고 있는 플랫폼에 맞추어
변환하게 된다. 이는 setup script 가 운영체제에 구애받지 않고 이식성을 갖게 해준다. 이에 따라서 본 문서의 모든
패스이름들은 / 로 구분되어 질 것이다.
물론 이는 Distutils 함수에 제공되는 패스이름에만 적용된다. 예를 들어, 당신이 팡일을 언급하기 위해서
glob.glob() oros.listdir()와 같은 표준 Python 함수들을 사용한다면, 하드코딩된 패스 구분자보다는
이식가능한 코드를 작성하도록 주의해야 한다.
glob.glob(os.path.join('mydir', 'subdir', '*.html'))
os.listdir(os.path.join('mydir', 'subdir')
2.1 Listing whole packages
패키지들 옵션은 Distutils 에게 패키지들 리스트에서 언급된 각각의 패키지에서 찾을 수 있는 모든 pure
Python 모듈들에 대해서 어떠한 처리를 할 지 말하게 된다. 이를 위해서, 물론, 패키지 이름과 파일시스템상에서의
디렉토리간의 대응점이 있어야만 한다. 기본 대응점은 가장 명확한 것 (예를 들어 distutils 패키지는 배포판 루트에 대해서
distutils 디렉토리안에서 찾을 수 있다.)이 된다. 그러므로, 개발자가 packages = ['foo'] 라고 setup
script 에서 언급했을때, 개발자는 Distutils 에게 setup script 가 존재하는 디렉토리를 기반으로
foo/__init__.py를 찾을 수 있다고 약속하는 것과 같은 것이다. 만약 개발자가 이 약속을 저버린다면,
Distutils 는 warning 을 발할 것이고, 깨진 패키지를 생산하게 된다.
만약 개발자가 소스디렉토리의 구조레 대해서 다른 컨벤션을 사용하고 싶다면, 이를 지원하지 위한 방안이 있다: 개발자는 단지
package_dir 옵션을 이용해서 원하는 컨벤션을 이야기하면 된다. 예를 들어, lib 아래의 모든 Python 소스를
유지하고 싶다면, 그래서 해당 모듈들이 어떠한 패키지에도 속하지 않는 "root package" 가 lib 안에 있고, foo
패키지안의 모듈들이 lib/foo에 있다면.. 등일때 setup script 에서 다음과 같이 package_dir 을 사용한다
package_dir = {'': 'lib'}
이 사전의카들은 패키지 이름들과 root 패키지를 위한 공란의 패키지 이름이다. 값들은 배포판 root 의 상대적인
디렉토리 이름이 된다. 이 경우에는, pckages = ['foo']라고 말한다면, 개발자는 lib/foo/__init__.py
가 존재함을 약속하는 것이다.
또다른 가능한 컨벤션은 lib 바로 아래 foo 패키지를 넣고, foot.bar 패키지를 lib/bar 에 넣는 것이다. 이를 위해서는 setup script 는 다음과 같이
package_dir = {'foo': 'lib'}
와 같이 기술되어야 한다. package_dir 사전 안의 package: dir 엔트리는 묵시적으로 package 아래의
모든 패키지들에 적용된다. 그래서 foo.bar 경우는 자동적으로 이에 의해서 다루어진다. 이 예제에서, packages =
['foo', 'foo.bar']를 갖는 것은 Distutils 에게 lib/__init__.py와
lib/bar/__init__.py 를 살펴보라고 말하게 되는 것이다. (명시해야 할 것은 package_dir 이 순환적으로
적용된다해도, 개발자는 명시적으로 패키지들의 모든 패키지의 리스트를 작성해야한다: Distutils 는 __init__.py 가
있는 어떠한 디렉토리를 찾기 위해서 소스트리를 순환적으로 스캔하지는 않기 때문이다.)
2.2 Listing individual modules
작은 모듈을 배포하기 위해서, 개발자는 패키지를 리스팅하기보다는 모든 모듈들을 리스ㄹ팅하기를 선호할 수 있다 (특히
"root package"안의 하나의 모듈만 존재하는 경우). 이러한 간단한 경우는 1.2 장에서 나왔는데 이를 간단히 더
살펴보다면:
py_modules = ['mod1', 'pkg.mod2']
이는 두개의 모듈들을 설명한다. 하나는 "root" 패키지에 존재하고, 다른 하나는 pkg 패키지에 존재한다. 다시
말하자면, 기본 패키지/디렉토리 구조는 이러한 두개의 모듈들이 mod1.py 와 pkg/mod2.py에서 찾을 수 있음을
의미하고, pkg/__init__.py 가 역시 존재할 것임을 의미한다. 그리고 또한 개발자는 package/directory
대응용 package_dir 옵션을 이용해서 오버라이드할 수 있다.
2.3 Describing extension modules
2.4 Relationships between Distributions and Packages
2.5 Installing Scripts
지금까지 우리는 자신에 의해서 수행되지는 않지만 script들에 의해서 import 되는 pure 와 non-pure Python 모듈들을 다뤄왔다.
Script들은 커맨드 라인으로부터 실행되도록 의도된 Python source code 를 담고 있는 파일들이다.
Script들은 Distutils이 뭔가 복잡한 일을 하도록 요구하지 않는다. 오직 괜찮은 특징은 만약 스크립트의 첫번째 라인이
#!로 시작하고 python을 담고 있는다면, Distutils는 단지 첫라인을 현재 ㄹ인터프리터의 로케이션을 언급하도록 조정할
뿐이다. 깁ㄴ적으로 이는 현재 인터프리터 위치로 바뀐다. --executable 옵션은 인터프리터 패스가 명시적으로
오버라이드되도록 허용한다.
scripts 옵션은 간단히 이러한 방법으로 다뤄지는 파일들의 리스트를 보여준다. 예를 들어 PyXML setup script에는 다음처럼 나와있다.
setup(...
scripts=['scripts/xmlproc_parse', 'scripts/xmlproc_val']
)
2.6 Installing Package Data
종종 추가적인 파일이 패키지에 설치될 필요가 있다. 이러한 파일들은 종종 패키지들의 구현에 매우 관련된 데이터 혹은
프로그래머가 패키지를 사용함에 있어서 흥미있어할 만한 문서들을 담고 있는 텍스트 파일이기도 하다. 이러한 파일들은 package
data 라 불리운다.
패키지 데이터는 package 들에 package_data 키워드 아규먼트를 이용해서 setup 함수에 추가된다. 값은
반드시 패키지 이름으로부터 패키지에 카피되어야하는 상대 패스 이름들의 목록으로의 사상이 되어야 한다. 패스들은 패키지를 포함하는
디렉토리 (package_dir 매핑으로부터 나온 정보가 사용되기도 한다.) 에 상대경로써 해석된다; 즉, 파일들은 소스
디렉토리들 내의 패키지의 부분으로 예상된다. 그것들은 glob 패턴들 이용해서 포함될 수 있다.
패스 이름들은 디렉토리의 일부분들을 포함할 수 있다; 어떤 필요한 디렉토리들은 설치시에 생생된다.
예를 들어, 만약 패키지가 몇몇 데이터파일들을 갖는 서브디렉토리를 포함해야한다고 한다면, 파일들은 소스트리내에서 다름과 같이 정리될 수 있다:
setup.py
src/
- mypkg/
- - __init__.py
- - module.py
- - data/
- - - tables.dat
- - - spoons.dat
- - - forks.dat
이에 대응되는 setup() 함수로의 호출은 다음과 같이 될 수 있다.
setup(...,
package_dir={'mypkg': 'src/mypkg'},
package_data={'mypkg': ['data/*.dat']},
)
이는 2.4 버전에서부터 추가되었다.
2.7 Installing Additional Files
data_files 옵션은 모듈 배포에 필요한 추가적인 파일들 (설정 파일들, 메시지 카탈로그들, 데이터 파일들, 혹은 이전의 카테고리에 속하지 않는 어떠한 파일이든지...)을 열거하는데 사용될 수 있다.
data_files 는 다음과 같은 방법으로 (directory, files) 쌍의 순서를 열거한다.
setup(...
data_files=[('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
('config', ['cfg/data.cfg']),
('/etc/init.d', ['init_script'])]
)
눈여겨 봐야할 것은 개발자는 데이터파일들이 설치될 디렉토리의 이름을 열거할 수 있다는 것이다. 그렇지만 데이터파일 자체의 이름을 변경하지는 못한다.
나열된 각 (directory, files) 쌍은 설치 디렉토리와 그안에 설치될 파일들을 열거한다. 만약 directory
가 상태 패스라면, 설치 프리픽스 (이때 pure_Python 패키지에 대해서는 Python의 sys.prefix,
extension modules 를 포함한 패키지는 sys.exec_prefix)에 대해 상대적으로 해석된다. files 안의
각각의 파일 이름은 패키지 소스 배포판의 최상단에 있는 setup.py script 에 대해서 상대적으로 해석된다. files의
디렉토리 정보는 설치된 파일의 최종 위치를 알아내는데 사용되지 않고, 오직 파일의 이름만이 사용된다.
개발자는 data_files 옵션들을 대상 디렉토리 없이 파일들의 이름만으로 간단히 열거할 수 있으나, 이러한 방법은
추천하지 않는다. install 커맨드는 이러한 경우에 대해서는 warning 을 프린트할 것이다. 대상 디렉토리에 직접적으로
data files를 설치하려면 디렉토리로서 빈 문자열이 제공되어야 한다.
2.8 Additional meta-data
setup script 는 이름, 버전 이되에 추가적인 meta-data 를 포함할 수 있다. 이러한 정보는 다음을 포함한다.
| Meta-Data |
Description |
Value |
Notes |
name |
name of the package |
short string |
(1) |
version |
version of this release |
short string |
(1)(2) |
author |
package author's name |
short string |
(3) |
author_email |
email address of the package author |
email address |
(3) |
maintainer |
package maintainer's name |
short string |
(3) |
maintainer_email |
email address of the package maintainer |
email address |
(3) |
url |
home page for the package |
URL |
(1) |
description |
short, summary description of the package |
short string |
|
long_description |
longer description of the package |
long string |
|
download_url |
location where the package may be downloaded |
URL |
(4) |
classifiers |
a list of classifiers |
list of strings |
(4) |
위 테이블에 대해서
(1) 이 필드들은 필수적으로 필요하다.
(2) 이 필드들은 버젼이 major.minor[.patch[.sub]]의 형태를 띨 경우 추천된다.
(3) author 혹은 maintainer 가 확인되어야만 한다.
(4) 만약 개발한 패키지가 2.2.3 혹은 2.2.3 앞의 Python version들에 호환된다면, 이 필드들은 사용되지 않는다. 목록은 PyPI 웹사이트에서 찾을 수 있다.
'short string' 200 글자를 넘지 않는 텍스트의 하나의 라인
'long string' reStructuredText 포맷을 따르는 plain text 의 다수 라인들
'list of strings' 아래를 참조
string 값들이 유니코드가 아닐 수도 있다.
버전 정보를 인코딩하는 것은 그 나름대로의 규칙이 있다. Python 패키지들은 일반적으로 버전 포맷으로
major.minor[.patch][.sub]를 추가한다. major 번호는 초기, 실험적 릴리즈에 대해서는 0을 사용한다.
그리고 패키지의 주요 마일스톤이 나타날 때, 릴리즈에 대해서 숫자를 올려나간다. minor 숫자는 패키지에 중요한 특징이 추가될
때 증가된다. 패치 번호는 bug-fix 릴리즈가 행해질 때 증가된다. 추가적으로 뒤따라오는 버전 정보는 종종
sub-releases를 나타내기 위해서 사용된다. 이들은 기능성과 API 가 변한 알파릴리즈에 대해서는 "a1, a2,
..., aN", 버그들만을 수정한 beta 릴리즈에 대해서는 "b1, b2, ... bN", 그리고 테스팅을 위한
pre-release 릴리즈에 대해서는 "pr1, pr2, ... prN" 이 붙는다.
2.8 Additional meta-data
분류자는 다음과 같이 python list 에 의해서 열거된다:
setup(...
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Console',
'Environment :: Web Environment',
'Intended Audience :: End Users/Desktop',
'Intended Audience :: Developers',
'Intended Audience :: System Administrators',
'License :: OSI Approved :: Python Software Foundation License',
'Operating System :: MacOS :: MacOS X',
'Operating System :: Microsoft :: Windows',
'Operating System :: POSIX',
'Programming Language :: Python',
'Topic :: Communications :: Email',
'Topic :: Office/Business',
'Topic :: Software Deveopment :: Bug Tracking',
],
)
만약 당신이 setup.py 파일에 분류자를 포함하기를 원한다면, 그리고 역시 Python 2.2.3 이전 버전에 대해서 하위-호환성을 제공하고 싶다면, 당신은 다음 코드 부분을 setup() 호출 이전에 포함해야 한다.
# patch distutils if it can't cope with the "classifiers" or
# "download_url" keywords
from sys import version
if version < '2.2.3':
from distutils.dist import DistributionMetadata
DistributionMetadata.classifiers = None
DistributionMetadata.download_url = None
2.9 Debugging the setup script
종종 뭔가 잘못될 수 있는데, 이때 setup script가 개발자가 원하는 것을 해주지는 않는다.
Distutils 는 setup script 르 수행할 때 예외들을 잡고, script 를 끝내기 전에 간단한 에러
메시지를 프린트한다. 이러한 행동의 모티베이션은 Ptyhon에 대해서 잘 알지 못하는 관리자로 하여금 혼동하지 말고 패키지를
설치를 시도하라는 것을 말한다. 만약 Distutils 가 Distutils 의 깊은 곳으로부터의 기나긴 traceback을
제공한다면 사용자는 아래서부터 읽어서 단순한 퍼미션 문제임을 확인하지 못하고 패키지나 Python 설치가 깨졌다고 생각하게 될
것이다.
다시 말하자면, 이는 개발자로 하여금 문제의 원인을 찾도록 돕지는 않는다. 이러한 목적으로 위해서
DISTUTILS_DEBUG 환경 변수가 있는데 빈 문자열이 아닌 아무 문자역이 설정되면 distutils 는 무엇을 하는지와
에러가 발생한 경우에 대해서 완전한 traceback을 출력하게 된다.