Python 프로젝트를 패키지로 만들기 with setup.py

d4v1d·2022년 3월 10일
2
post-thumbnail

개요

Python 프로젝트를 배포하기 위해서는 프로젝트를 패키지화해야 합니다. setuptoolssetup.py를 이용해 Python 프로젝트를 패키지화하고 설치하는 방법을 다룬 포스트입니다. 🐍

Getting Started

참고자료 - 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'

환경변수 PYTHONPATHsys.path에 해당 경로를 추가해주는 방법이 있지만, 이는 서로 다른 환경에서 잘못 작동할 여지가 많기 때문에 권장하는 방법이 아니라고 합니다.
이러한 문제를 해결하기 위해 setup.pysetuptools, 그리고 pip를 이용하여 패키지를 설치합니다.

Minimal Example

setup.py

from setuptools import setup, find_packages

setup(
    name='example',
    version='0.1.0',
    packages=find_packages(include=['exampleproject', 'exampleproject.*'])
)

namepip가 이 패키지를 참조하는 이름입니다. 즉, 이 패키지의 디렉터리 이름은 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도 같이 설치!

Wrap-up Example

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등을 통해 배포할 수 있습니다. 🐢

profile
데이터 엔지니어/백엔드 개발자 d4v1d의 개발 일지🐯

3개의 댓글

comment-user-thumbnail
2023년 7월 26일

도움 많이 되고 있습니다! 질문이 하나 있는데요,

from exampleproject import example # module을 import
/example_project/에 위치한 코드는 위 모듈을 이상 없이 import하겠지만, 그 외의 경로에서는 아마 아래와 같은 에러가 발생할 것입니다.

라고 하셨는데, 그 이유가 뭔지 알 수 있을까요?
from example~~ 로 시작하는 import는 절대 임포트라고 알고 있는데, 그러면 다른 디렉토리에 위치한 코드에서 import를 하더라도 exampleproject 최상단에서부터 시작하는 절대 경로로 import할 수 있는 게 아닌가요?

답글 달기
comment-user-thumbnail
2023년 8월 26일

정말 쉽게 설명을 잘해주셨네요

답글 달기
comment-user-thumbnail
2024년 4월 1일

setup.py는 현재는 사용이 권장되지 않고 새로운 프로젝트의 경우 pyproject.toml을 이용한 빌드가 권장된다는 점을 알아두세요.

답글 달기