https://code.tutsplus.com/ko/tutorials/how-to-write-your-own-python-packages--cms-26076



나만의 파이썬 패키지를 작성하는 법

by 
Difficulty:IntermediateLength:MediumLanguages:

Korean (한국어) translation by Dae-yeop Lee (you can also view the original English article)

파이썬은 훌륭한 프로그래밍 언어이자 훨씬 더 그 이상입니다. 하지만 파이썬의 약한 점 중 하나는 패키징입니다. 이것은 파이썬 커뮤니티에도 잘 알려진 사실입니다. 패키지의 설치, 임포트, 사용과 생성은 수년 동안 개선되긴 했지만 파이썬을 비롯해 다른 좀 더 성숙한 언어들을 반면교사 삼은 Go와 Rust 같은 새로운 언어와 여전히 같은 수준은 아닙니다.

이번 튜토리얼에서는 자신만의 패키지를 만들고 공유하는 데 필요한 모든 것을 배웁니다. 파이썬 패키지에 대한 일반적인 배경지식이 필요하다면 파이썬 패키지 사용하는 법을 참고합니다.

프로젝트 패키징은 응집성 있는 파이썬 모듈 및 기타 파일들을 가져와 손쉽게 사용할 수 있는 구조로 만드는 과정입니다. 이때 다른 패키지에 대한 의존성, 내부 구조(서브 패키지), 버전 관리, 대상 사용자, 패키지 형태(소스 및/또는 바이너리)와 같이 다양한 사항들을 고려해야 합니다.

Advertisement

간단한 예제로 시작해 봅시다. conman 패키지는 설정을 관리하기 위한 패키지입니다. 이 패키지는 etcd를 이용한 분산 설정뿐 아니라 여러 파일 형식을 지원합니다.

일반적으로 패키지의 내용은 단 하나의 디렉터리에 저장되며(하위 패키지를 여러 디렉터리로 분할하는 경우가 더 많긴 하지만) 때때로 이 경우처럼 자체적인 git 저장소에 저장되기도 합니다.

루트 디렉터리에는 다양한 설정 파일(setup.py는 필수이고 가장 중요한 파일임)이 들어 있으며, 패키지 코드 자체는 일반적으로 패키지와 이름이 같은 하위 디렉터리 및 이상적으로는 tests 디렉터리에 존재합니다. 다음은 "conman" 패키지의 파일 및 디렉터리 구성을 보여줍니다.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
> tree
 
.
 
├── LICENSE
 
├── MANIFEST.in
 
├── README.md
 
├── conman
 
│   ├── __init__.py
 
│   ├── __pycache__
 
│   ├── conman_base.py
 
│   ├── conman_etcd.py
 
│   └── conman_file.py
 
├── requirements.txt
 
├── setup.cfg
 
├── setup.py
 
├── test-requirements.txt
 
├── tests
 
│   ├── __pycache__
 
│   ├── conman_etcd_test.py
 
│   ├── conman_file_test.py
 
│   └── etcd_test_util.py
 
└── tox.ini

setup.py 파일을 살짝 살펴봅시다. 여기서는 setuptools 패키지에서 setup()과 find_packages()라는 두 가지 함수를 임포트합니다. 그런 다음 setup() 함수를 호출하고 매개변수 중 하나에 find_packages()를 사용합니다.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from setuptools import setup, find_packages
 
 
 
setup(name='conman',
 
      version='0.3',
 
 
      license='MIT',
 
      author='Gigi Sayfan',
 
      author_email='the.gigi@gmail.com',
 
      description='Manage configuration files',
 
      packages=find_packages(exclude=['tests']),
 
      long_description=open('README.md').read(),
 
      zip_safe=False,
 
      setup_requires=['nose>=1.0'],
 
      test_suite='nose.collector')

보다시피 아주 평범합니다. setup.py 파일은 일반적인 파이썬 파일이라서 이 파일 안에서 원하는 모든 작업을 수행할 수 있지만 이 파일의 주된 작업 setup() 함수를 적절한 매개변수와 함께 호출하는 것입니다. 왜냐하면 패키지를 설치할 때 다양한 도구에서 표준화된 방식으로 이 함수가 호출될 것이기 때문입니다. 다음 절에서 자세한 내용을 살펴보겠습니다.

setup.py 말고도 여기서 보여드리거나 다양한 목적으로 사용될 수 있는 몇 가지 다른 선택적인 설정 파일이 있습니다.

setup() 함수는 다양한 명령을 실행하는 것과 더불어 패키지 설치의 여러 측면을 제어하기 위한 명명된 인수를 여러 개 받습니다. 대부분의 인수는 패키지를 저장소에 업로드할 때 검색 및 필터링에 사용되는 메타데이터를 지정하는 역할을 합니다.

  • name: 패키지 이름(그리고 PYPI에 어떻게 나열될지를 지정)
  • 버전: 적절한 의존성 관리를 유지하는 데 중요합니다.
  • url: 패키지 URL. 일반적으로 깃허브(GitHub) 또는 readthedocs URL에 해당합니다.
  • packages: 패키지에 포함해야 할 서브 패키지 목록. 이때 find_packages()가 활용됩니다.
  • setup_requires: 이곳에 의존성을 지정합니다.
  • test_suite: 테스트 시 실행할 도구

여기서는 long_description을 README.md 파일의 내용으로 설정했는데, 이처럼 한곳에서 필요한 내용을 작성하고 이를 참조하는 것이 좋습니다.

setup.py 파일은 다양한 명령을 실행하기 위한 명령행 인터페이스도 제공합니다. 예를 들어, 단위 테스트를 실행하려면 python setup.py test라고 입력하면 됩니다.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
running test
 
running egg_info
 
writing conman.egg-info/PKG-INFO
 
writing top-level names to conman.egg-info/top_level.txt
 
writing dependency_links to conman.egg-info/dependency_links.txt
 
reading manifest file 'conman.egg-info/SOURCES.txt'
 
reading manifest template 'MANIFEST.in'
 
writing manifest file 'conman.egg-info/SOURCES.txt'
 
running build_ext
 
test_add_bad_key (conman_etcd_test.ConManEtcdTest) ... ok
 
test_add_good_key (conman_etcd_test.ConManEtcdTest) ... ok
 
test_dictionary_access (conman_etcd_test.ConManEtcdTest) ... ok
 
test_initialization (conman_etcd_test.ConManEtcdTest) ... ok
 
test_refresh (conman_etcd_test.ConManEtcdTest) ... ok
 
test_add_config_file_from_env_var (conman_file_test.ConmanFileTest) ... ok
 
test_add_config_file_simple_guess_file_type (conman_file_test.ConmanFileTest) ... ok
 
test_add_config_file_simple_unknown_wrong_file_type (conman_file_test.ConmanFileTest) ... ok
 
test_add_config_file_simple_with_file_type (conman_file_test.ConmanFileTest) ... ok
 
test_add_config_file_simple_wrong_file_type (conman_file_test.ConmanFileTest) ... ok
 
test_add_config_file_with_base_dir (conman_file_test.ConmanFileTest) ... ok
 
test_dictionary_access (conman_file_test.ConmanFileTest) ... ok
 
test_guess_file_type (conman_file_test.ConmanFileTest) ... ok
 
test_init_no_files (conman_file_test.ConmanFileTest) ... ok
 
test_init_some_bad_files (conman_file_test.ConmanFileTest) ... ok
 
test_init_some_good_files (conman_file_test.ConmanFileTest) ... ok
 
 
 
----------------------------------------------------------------------
 
Ran 16 tests in 0.160s
 
 
 
OK

setup.cfg는 ini 형식의 파일로서 setup.py에 전달하는 명령에 대한 옵션 기본값을 담을 수 있습니다. 여기서 setup.cfg는 nosetests(테스트 러너)에 대한 몇 가지 옵션을 담고 있습니다.

1
2
3
4
5
[nosetests]
 
verbose=1
 
nocapture=1

이 파일에는 내부 패키지 디렉터리에 들어있지는 않지만 포함시키고 싶은 파일을 담습니다. 일반적으로 이러한 파일로 readme 파일이나 라이선스 파일 및 이와 비슷한 파일들이 있습니다. 중요한 파일은 requirements.txt입니다. 이 파일은 pip가 다른 필수 패키지를 설치하는 데 사용됩니다.

conman의 MANIFEST.in 파일은 다음과 같습니다.

1
2
3
4
5
include LICENSE
 
include README.md
 
include requirements.txt

의존성은 setup.py의 install_requires 섹션과 requirements.txt 파일에 모두 지정할 수 있습니다. pip는 install_requires의 의존성은 자동으로 설치하지만 requirements.txt 파일의 의존성은 자동으로 설치하지 않습니다. 그러한 요구사항을 설치하려면 pip install -r requirements.txt와 같이 pip를 실행할 때 명시적으로 지정해야 합니다.

install_requires 옵션은 메이저 버전 수준에서 최소한의 추상적인 요구사항을 지정하기 위해 고안됐습니다. requirements.txt 파일은 고정된 마이너 버전을 이용해 좀 더 구체적인 요구사항을 지정하기 위한 것입니다.

conman의 요구사항 파일은 다음과 같습니다. 보다시피 모든 버전이 고정돼 있음을 알 수 있습니다. 이는 이러한 패키지 중 하나가 업그레이드되어 conman을 망가뜨리는 변경사항이 도입될 경우 부정적인 영향을 받을 수 있음을 의미합니다.

01
02
03
04
05
06
07
08
09
10
11
PyYAML==3.11
 
python-etcd==0.4.3
 
urllib3==1.7
 
pyOpenSSL==0.15.1
 
psutil==4.0.0
 
six==1.7.3

버전을 고정하면 예측 가능성과 마음의 평안을 확보할 수 있습니다. 이는 많은 사람들이 여러분의 패키지를 서로 다른 시기에 설치하는 경우에 특히 중요합니다. 버전을 고정하지 않으면 각 사용자가 설치한 시점에 따라 서로 다른 버전의 의존성을 얻을 수 있습니다. 버전 고정의 단점은 의존성 개발을 쫓아가지 않으면 오래되고 성능이 형편없으며, 심지어 취약한 의존성에 발목을 잡힐 수 있다는 것입니다.

나는 conman을 2014년에 처음으로 작성했고 크게 신경을 쓰지 않았다. 이제 이 튜토리얼에 쓰기 위해 모든 것을 업그레이드했으며 거의 모든 의존성 전반에 걸쳐 상당한 개선이 있었다.

여러분은 소스 배포판이나 바이너리 배포판을 만들 수 있습니다. 여기서는 둘 다 다루겠습니다.

소스 배포판을 만들려면 python setup.py sdist 명령을 이용합니다. 다음은 conman에 대한 출력 결과입니다.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
> python setup.py sdist
 
running sdist
 
running egg_info
 
writing conman.egg-info/PKG-INFO
 
writing top-level names to conman.egg-info/top_level.txt
 
writing dependency_links to conman.egg-info/dependency_links.txt
 
reading manifest file 'conman.egg-info/SOURCES.txt'
 
reading manifest template 'MANIFEST.in'
 
writing manifest file 'conman.egg-info/SOURCES.txt'
 
warning: sdist: standard file not found: should have one of README, README.rst, README.txt
 
 
 
running check
 
creating conman-0.3
 
creating conman-0.3/conman
 
creating conman-0.3/conman.egg-info
 
making hard links in conman-0.3...
 
hard linking LICENSE -> conman-0.3
 
hard linking MANIFEST.in -> conman-0.3
 
hard linking README.md -> conman-0.3
 
hard linking requirements.txt -> conman-0.3
 
hard linking setup.cfg -> conman-0.3
 
hard linking setup.py -> conman-0.3
 
hard linking conman/__init__.py -> conman-0.3/conman
 
hard linking conman/conman_base.py -> conman-0.3/conman
 
hard linking conman/conman_etcd.py -> conman-0.3/conman
 
hard linking conman/conman_file.py -> conman-0.3/conman
 
hard linking conman.egg-info/PKG-INFO -> conman-0.3/conman.egg-info
 
hard linking conman.egg-info/SOURCES.txt -> conman-0.3/conman.egg-info
 
hard linking conman.egg-info/dependency_links.txt -> conman-0.3/conman.egg-info
 
hard linking conman.egg-info/not-zip-safe -> conman-0.3/conman.egg-info
 
hard linking conman.egg-info/top_level.txt -> conman-0.3/conman.egg-info
 
copying setup.cfg -> conman-0.3
 
Writing conman-0.3/setup.cfg
 
creating dist
 
Creating tar archive
 
removing 'conman-0.3' (and everything under it)

보다시피 표준 접미어 중 하나가 붙은 README 파일이 누락됐다는 경고가 표시됐는데, 저는 마크다운을 좋아해서 "README.md"를 대신 만들어뒀기 때문입니다. 그 밖의 모든 패키지 소스 파일과 추가 파일이 포함됐습니다. 그런 다음 conman.egg-info 디렉터리에 여러 메타데이터가 만들어졌습니다. 마지막으로 conman-0.3.tar.gz라는 압축된 tar 아카이브가 생성되어 dist 하위 디렉터리에 저장됩니다.

이 패키지를 설치하려면 빌드 단계가 필요할 것입니다(심지어 순수 파이썬으로 만들어져 있더라도). 평소처럼 pip를 이용해 패키지 경로만 전달해도 해당 패키지를 설치할 수 있습니다. 예를 들면 다음과 같습니다.

1
2
3
4
5
6
7
8
9
pip install dist/conman-0.3.tar.gz
 
Processing ./dist/conman-0.3.tar.gz
 
Installing collected packages: conman
 
  Running setup.py install for conman ... done
 
Successfully installed conman-0.3

conman이 site-packages에 설치됐고 다른 여느 패키지와 마찬가지로 임포트할 수 있습니다.

1
2
3
4
5
import conman
 
conman.__file__
 
'/Users/gigi/.virtualenvs/conman/lib/python2.7/site-packages/conman/__init__.pyc'

휠(wheel)은 파이썬 코드와 선택적으로 C 확장을 패키징하는 비교적 새로운 방법입니다. 휠은 egg 형식을 대체합니다. 휠에는 순수 파이썬 휠, 플랫폼 휠, 범용 휠과 같은 몇 가지 유형이 있습니다. 순수 파이썬 휠은 conman과 같이 C 확장 코드가 없는 패키지를 말합니다.

플랫폼 휠에는 C 확장 코드가 있습니다. 범용 휠은 동일한 코드 기반으로 파이썬 2와 파이썬 3에 모두 호환되는 순수 파이썬 휠입니다(2to3조차도 필요하지 않음). 순수 파이썬 패키지가 있고 패키지가 파이썬 2와 파이썬 3(점점 더 중요해지고 있는)을 모두 지원하게 하려면 파이썬 2용 휠 하나와 파이썬 3용 휠 하나를 빌드하는 대신 단 하나의 범용 휠을 빌드하면 됩니다.

패키지에 C 확장 코드가 있는 경우 각 플랫폼용 플랫폼 휠을 빌드해야 합니다. 특히 C 확장이 포함된 패키지의 경우 휠의 커다란 이점은 대상 장비에서 컴파일러 및 지원 라이브러리를 사용할 필요가 없다는 것입니다. 이는 휠에 이미 빌드된 패키지가 포함돼 있기 때문입니다. 따라서 빌드가 실패하지 않으며, 빌드된 패키지를 단순히 복사하는 것이라서 설치 과정도 훨씬 빠릅니다. Numpy나 Pandas 같은 과학 라이브러리를 사용하는 사람들은 이러한 패키지를 설치하는 데 오랜 시간이 걸렸고, 일부 라이브러리가 누락됐거나 컴파일러가 제대로 설정되지 않은 경우 패키지 설치에 실패했을 수 있으므로 분명 이를 반가워할 것이다.

순수 휠 또는 플랫폼 휠을 빌드하는 명령은 python setup.py bdist_wheel입니다.

setup() 함수를 제공하는 엔진에 해당하는 Setuptools는 순수 휠 또는 플랫폼 휠이 필요한지 여부를 자동으로 감지합니다.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
running bdist_wheel
 
running build
 
running build_py
 
creating build
 
creating build/lib
 
creating build/lib/conman
 
copying conman/__init__.py -> build/lib/conman
 
copying conman/conman_base.py -> build/lib/conman
 
copying conman/conman_etcd.py -> build/lib/conman
 
copying conman/conman_file.py -> build/lib/conman
 
installing to build/bdist.macosx-10.9-x86_64/wheel
 
running install
 
running install_lib
 
creating build/bdist.macosx-10.9-x86_64
 
creating build/bdist.macosx-10.9-x86_64/wheel
 
creating build/bdist.macosx-10.9-x86_64/wheel/conman
 
copying build/lib/conman/__init__.py -> build/bdist.macosx-10.9-x86_64/wheel/conman
 
copying build/lib/conman/conman_base.py -> build/bdist.macosx-10.9-x86_64/wheel/conman
 
copying build/lib/conman/conman_etcd.py -> build/bdist.macosx-10.9-x86_64/wheel/conman
 
copying build/lib/conman/conman_file.py -> build/bdist.macosx-10.9-x86_64/wheel/conman
 
running install_egg_info
 
running egg_info
 
creating conman.egg-info
 
writing conman.egg-info/PKG-INFO
 
writing top-level names to conman.egg-info/top_level.txt
 
writing dependency_links to conman.egg-info/dependency_links.txt
 
writing manifest file 'conman.egg-info/SOURCES.txt'
 
reading manifest file 'conman.egg-info/SOURCES.txt'
 
reading manifest template 'MANIFEST.in'
 
writing manifest file 'conman.egg-info/SOURCES.txt'
 
Copying conman.egg-info to build/bdist.macosx-10.9-x86_64/wheel/conman-0.3-py2.7.egg-info
 
running install_scripts
 
creating build/bdist.macosx-10.9-x86_64/wheel/conman-0.3.dist-info/WHEEL<br>

dist 디렉터리를 살펴보면 순수 파이썬 휠이 생성됐음을 확인할 수 있습니다.

01
02
03
04
05
06
07
08
09
10
11
ls -la dist
 
 
 
dist/
 
total 32
 
-rw-r--r--  1 gigi  staff   5.5K Feb 29 07:57 conman-0.3-py2-none-any.whl
 
-rw-r--r--  1 gigi  staff   4.4K Feb 28 23:33 conman-0.3.tar.gz

"conman-0.3-py2-none-any.whl"이라는 이름에는 패키지 이름, 패키지 버전, 파이썬 버전, 플랫폼 버전, 그리고 마지막으로 "whl"이라는 확장자가 포함돼 있습니다.

범용 패키지를 빌드하려면 python setup.py bdist_wheel --universal처럼 --universal을 추가하기만 하면 된다.

그 결과로 만들어지는 휠은 "conman-0.3-py2.py3-none-any.whl"입니다.

참고로 범용 패키지를 만들 경우 코드가 파이썬 2와 파이썬 3에서 모두 실제로 작동하도록 보장하는 것은 여러분의 책임입니다.

다음의 포스트를 참고하세요! https://valuefactory.tistory.com/565?category=765138

나만의 파이썬 패키지를 작성하려면 많은 도구를 다루고, 여러 메타데이터를 지정하며, 의존성과 대상 사용자에 관해 곰곰이 생각해봐야 합니다. 하지만 그 결과로 얻는 보상은 큽니다.

유용한 코드를 작성하고 이를 적절히 패키징하면 사람들이 이를 손쉽게 설치하고 이로부터 혜택을 얻을 수 있을 것입니다.




+ Recent posts