Python 프로젝트를 배포하기 위해서는 프로젝트를 패키지화해야 합니다. setuptools와
setup.py
를 이용해 Python 프로젝트를 패키지화하고 설치하는 방법을 다룬 포스트입니다. 🐍
참고자료 - A Practical Guide to Using Setup.py
내가 생성한 Python 프로젝트의 디렉터리 구조가 다음과 같다고 가정해봅시다.
example_project/
├── exampleproject/ Python package with source code.
│ ├── __init__.py Make the folder a package.
│ └── example.py Example module.
└── README.md README with info of the project.
example
이라는 모듈을 외부에서 사용하고 싶으면, 아마 아래와 같이 모듈을 import할 겁니다.
from exampleproject import example # module을 import
from exampleproject.example import example_function # module 내 특정 함수만을 import
/example_project/
에 위치한 코드는 위 모듈을 이상 없이 import하겠지만, 그 외의 경로에서는 아마 아래와 같은 에러가 발생할 것입니다.
ModuleNotFoundError: No module named 'exampleproject'
환경변수 PYTHONPATH
나 sys.path
에 해당 경로를 추가해주는 방법이 있지만, 이는 서로 다른 환경에서 잘못 작동할 여지가 많기 때문에 권장하는 방법이 아니라고 합니다.
이러한 문제를 해결하기 위해 setup.py
와 setuptools, 그리고 pip
를 이용하여 패키지를 설치합니다.
setup.py
from setuptools import setup, find_packages
setup(
name='example',
version='0.1.0',
packages=find_packages(include=['exampleproject', 'exampleproject.*'])
)
name은 pip
가 이 패키지를 참조하는 이름입니다. 즉, 이 패키지의 디렉터리 이름은 exampleproject
이지만 pip install example
로 설치하게 된다는 뜻이죠! version은 이 패키지의 버전으로, PyPI에 배포할 때에도 사용되는 버전입니다. 마지막으로 packages에는 setuptools에 들어있는 find_packages
라는 모듈을 사용해서, 이 패키지에 포함하거나 제외할 파일들을 선택할 수 있습니다.
install
이제 /example_project/
디렉터리에서 다음 명령을 실행합니다.
pip install -e .
# -e: editable mode
# . : setup.py의 위치
setup.py
를 수정하면 패키지를 다시 설치해야 하는데, 복잡할 것 없이 pip install -e .
만 다시 실행하면 변경된 사항을 반영하여 패키지를 재설치할 수 있습니다.
Requirements
프로젝트의 dependencies들을 보통 requirements.txt
또는 environmenta.yml
로 관리합니다. setup.py
에서는 install_requires
인자로 dependencies를 명시할 수 있습니다.
setup(
name='example',
version='0.1.0',
packages=find_packages(include=['exampleproject', 'exampleproject.*']),
install_requires=[
'PyYAML', # version 명시 안 함
'pandas==0.23.3', # 정확한 version 명시
'numpy>=1.14.5', # 최소 version 명시
'matplotlib>=2.2.0',
'jupyter'
]
)
extras-require
어떤 dependencies를 특정 상황에서만 설치하고 싶은 경우, install_requires
대신 extras-require
로 명시하면 됩니다.
setup(
name='example',
version='0.1.0',
packages=find_packages(include=['exampleproject', 'exampleproject.*']),
install_requires=[
'PyYAML',
'pandas==0.23.3',
'numpy>=1.14.5'
],
extras_require={
'interactive': ['matplotlib>=2.2.0,, 'jupyter'],
}
)
pip install -e . # install_requires만 설치
pip install -e .[interactive] # extras_require도 같이 설치!
pip install -e example[interactive] # extras_require도 같이 설치!
setup.py
from setuptools import setup, find_packages
setup(
name='example',
version='0.1.0',
description='Setting up a python package',
author='david rhee',
author_email='d4v1d@my-email.com',
url='https://blog.d4v1d.com/setup-py',
packages=find_packages(include=['exampleproject', 'exampleproject.*']),
install_requires=[
'PyYAML',
'pandas==0.23.3',
'numpy>=1.14.5'
],
extras_require={'plotting': ['matplotlib>=2.2.0', 'jupyter']},
setup_requires=['pytest-runner', 'flake8'],
tests_require=['pytest'],
entry_points={ # my-command 라는 커맨드 명령어로 example.py의 main 함수 실행
'console_scripts': ['my-command=exampleproject.example:main']
},
package_data={'exampleproject': ['data/schema.json']}
)
setup.py
직접 실행 (권장 X)python setup.py install # 수정 없이 설치만 할 때
python setup.py develop # 개발(수정) 단계에서의 설치
위 방법으로 프로젝트 패키징을 할 수는 있지만, pip
를 사용한 설치가 아니기 때문에 pip uninstall
을 사용할 수 없어서 권장하지 않습니다.
requirements.txt
? setup.py
?참고자료 - requirements.txt vs setup.py
필요한 dependencies를 설치하기 위해 requirements.txt
를 만드는 방법도 있는데, setup.py
를 이용한 패키징과의 차이점은 무엇일까요?
requirements.txt
사실 이름이 requirements.txt
가 아니어도 됩니다. 하지만 관례적으로 이 이름을 쓰고 있습니다. 또한, 목적에 따라 dev-dependencies.txt
또는 test-dependencies.txt
와 같이 여러 개의 txt파일을 만들고 필요한 dependencies만 설치할 수도 있죠! 하나의 dependencies 파일 안에 다른 dependencies 파일이 들어있을 수 있습니다.
pip freeze > requirements.txt # 기본으로 제공되지 않는 패키지 목록을 requirements.txt에 출력
pip install -r requirements.txt # requirements.txt에 적힌 패키지 목록을 전부 설치
setup.py
나의 Python 프로젝트를 패키지로 정의하기 위해 setuptools를 이용하여 패키징하기 위해서는 setup.py
가 필요합니다. requirements.txt
가 단순히 프로젝트의 dependencies만을 나타낸 파일이라면, setup.py
에는 패키지 이름, 코드 파일, 메타데이터, 설치 등에 관한 정보들이 포함되어있습니다.
pip install -e .
참고자료 - Using Python setup.py
참고자료 - 파이썬 프로젝트의 구조
setuptools를 이용해 디렉터리 구조에 얽매이지 않고 Python 프로젝트를 패키지화하고, PyPI나 GitHub등을 통해 배포할 수 있습니다. 🐢
도움 많이 되고 있습니다! 질문이 하나 있는데요,
from exampleproject import example # module을 import
/example_project/에 위치한 코드는 위 모듈을 이상 없이 import하겠지만, 그 외의 경로에서는 아마 아래와 같은 에러가 발생할 것입니다.
라고 하셨는데, 그 이유가 뭔지 알 수 있을까요?
from example~~ 로 시작하는 import는 절대 임포트라고 알고 있는데, 그러면 다른 디렉토리에 위치한 코드에서 import를 하더라도 exampleproject 최상단에서부터 시작하는 절대 경로로 import할 수 있는 게 아닌가요?