Python) 파이썬 프로젝트를 패키지화하기(setup.py)

2021. 7. 27. 20:38꿀팁 분석 환경 설정/파이썬 개발 팁

일반적으로 우리가 분석을 하다 보면, 다양한 함수나 클래스들을 정의하게 된다. 

이럴 경우 가장 귀찮은 경우가 이러한 함수들을 매번 관리하는 것이나, 호출해오는데 많은 어려움이 있다.

 

아래와 같이 코드를 조직화한다고 해보자.

src/
    plot.py
    process.py
notebooks/
    exploration.ipynb

 

보통 자신의 만든 함수를 이용하려면 같은 디렉터리에 있어야 가능하지만, src에 있는 패키지를 사용하고 위해서는 아래와 같은 코드가 필요하다. 

기본적으로 특정 환경에서 site-package에 있는 것을 라이브러리를 불러오는 방식으로 되어있기 때문에 src에 있는 것을 불러오기 위해서는 필요하다.

import sys
sys.path.append('../src')

그렇지만, 우리가 매번 작업을 하다 보면, 폴더의 디렉터리를 변경할 경우가 있고, 이것을 특정 사람들에게 공유를 한다고 할 때도 디렉터리가 일치하지 않으면, 작동하지 않는다는 단점이 있다. 

 

Python 패키지는 pip install {package}을(를) 사용하여 설치할 수 있는 Python 파일의 모음이다.

pip은 코드를 찾을 위치를 알 수 있도록 Python 구성을 처리해야 한다.

바로 패키지 생성은 간단합니다. setup.py 파일만 추가하면 됩니다.

 

## setup.py
from glob import glob
from os.path import basename, splitext
from setuptools import find_packages, setup

setup(
    name='my_package',
    version='0.1',
    packages=find_packages(where='src'),
    package_dir={'': 'src'},
    py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')],
)

그때, 이런 식으로 구조를 맞춰주는 것이 좋다.

레이아웃을 약간 변경하여 my_package/ 디렉토리에 코드를 넣고 __init_. py 파일을 추가한다.

src/
    my_package/
        __init__.py
        plot.py
        process.py
notebooks/
    exploration.ipynb
setup.py

 

setup.py가 잇는 폴더에서 그런 다음에 아래 코드를 돌리면 된다

 

우리는 계속 수정을 할 것이니 develop 가 맞을 것이다.

 python setup.py develop

직접 개발 / 수정 / 디버그를 안한다고 하면 다음과 같다.

 python setup.py install

 

 

 

왼쪽은 install 하는 경우 / 오른쪽은 develop 하는 경우 

 

 

각각의 라이브러리가 제공될 때 문제 없이 배포될 수 있도록 패키지는 일련의 메타데이터를 포함하게 된다. 이 메타데이터에는 명칭, 버전, 의존성 등을 적게 된다. 라이브러리에 메타데이터를 작성할 수 있도록 다음과 같은 형식을 setup.py파일에서 사용할 수 있다.

 

# all of these work regardless of the current working directory!
import my_package
from my_package import plot
from my_package.plot import some_plotting_function

 

 

meta data를 넣을 수 있는 것이 너무 많아서 링크로 대체한다.

https://setuptools.readthedocs.io/en/latest/references/keywords.html

 

Keywords - setuptools 57.4.0 documentation

Previous Package Discovery and Resource Access using pkg_resources

setuptools.readthedocs.io

 

대충 조금 더 넣은다 하면 이 정도 넣어주면 될 것 같다.

 

## setup.py
from glob import glob
from os.path import basename, splitext
from setuptools import find_packages, setup

setup(
    name='my_package',
    version='0.1',
    author='aaa',
    author_email='aaa@bbb.com',
    #long_description=read('README.md'),
    #python_requires='>=3.6',
    #install_requires=['numpy'],
    #package_data={'mypkg': ['*/requirements.txt']}, # 아직 잘 모르겠음.
    #dependency_links = [], ## 최신 패키지를 설치하는 경우 
    description='my project',
    packages=find_packages(where='src'),
    package_dir={'': 'src'},
    py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')],
)

예제는 다음과 같다.

  • 모듈 의존성 관리 — install_requires
  • 비공개 모듈 설치 — dependency_links
  • 콘솔 스크립트 설치 — entry_points (아직 잘 모르겠다...)
  • 개발 모드 디플로이 — setup.py develop
from setuptools import setup, find_packages

setup_requires = [
    ]

install_requires = [
    'django==1.6b4',
    'markdown==2.3.1',
    'pyyaml==3.10',
    'pillow==2.1.0',
    'lxml==3.2.3',
    'beautifulsoup4==4.3.1',
    ]

dependency_links = [
    'git+https://github.com/django/django.git@stable/1.6.x#egg=Django-1.6b4',
    ]

setup(
    name='Flowdas-Books',
    version='0.1',
    description='Flowdas Books',
    author='Flowdas',
    author_email='spammustdie@flowdas.com',
    packages=find_packages(),
    install_requires=install_requires,
    setup_requires=setup_requires,
    dependency_links=dependency_links,
    scripts=['manage.py'],
    entry_points={
        'console_scripts': [
            'publish = flowdas.books.script:main',
            'scan = flowdas.books.script:main',
            'update = flowdas.books.script:main',
            ],
        },
    )

글 작성자 분의 설명은 다음과 같다.

http://www.flowdas.com/blog/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-setuptools/

 

설명은 다음과 같다.

해당 글을 참고하시면 더욱 도움이 되니 꼭 들어가서 확인해주시길 바랍니다. 

setup(), find_packages()

Setuptools에서 제공하는 두 개의 함수 setup()과 find_packages()를 사용합니다. setup() 은 표준 라이브러리에 포함된 Distutils의 setup()을 대체하는 것입니다. Distutils의 것에 비해 더 다양한 옵션들을 처리합니다.

find_packages()

예전에는 packages 옵션에 패키지 이름들을 일일이 적어주었습니다. 최상단 패키지만 적어주어야 한다면 크게 문제 될 것이 없지만, 하위 패키지들을 많이 만드는 방식의 프로그래밍을 하시는 분들은 영 불편해합니다. Setuptools 가 제공하는 find_packages() 함수는 폴더를 뒤져서 패키지들의 목록을 만들어 줍니다. 혹 뒤져야 할 폴더가 다른 곳에 있거나, 포함시키지 말아야 할 패키지가 있다면 인자로 지정할 수 있습니다.

install_requires

가장 중요한 부분은 이 프로젝트가 사용하는 패키지들을 지정하는 것인데, install_requires 옵션을 사용합니다. 전부 6개의 패키지를 지정했는데, 모두 django==1.6b4와 같이 구체적인 버전을 지정했습니다. 처음부터 버전을 지정하지는 않고, 어느 정도 호환성 확인이 된 후에 버전을 확인하여 고정하게 됩니다. 버전 확인에는 pip freeze 명령을 사용하면 됩니다. 패키지 이름의 대소문자는 중요하지 않습니다.

setup_requires

install_requires에 지정하는 것은 프로젝트를 위해 필요한 패키지들인데, 때로 setup.py 자신을 위해 필요한 패키지가 있을 수 있습니다. 이런 패키지들은 setup_requires 옵션을 사용하여 지정합니다. 이런 경우에 해당하는 패키지의 예는 setuptools_git 가 있습니다만, 저희는 사용하지 않기 때문에 자리만 마련하고 비워둡니다.

dependency_links

이 글을 쓰는 시점에 PyPI에 등록된 장고의 최신 버전은 1.5.4입니다. 때문에 위에서 지정한 1.6b4 버전은 PyPI에서 발견할 수 없고, 당연히 setup.py를 실행할 때 오류가 발생합니다. dependency_links 옵션은 PyPI 외의 장소에 있는 패키지를 설치할 수 있도록 합니다. 여러 가지 형태가 있습니다만 git 저장소를 사용하는 방법을 예시합니다. 장고의 git 저장소 주소인 https://github.com/django/django.git 앞에 git+ 를 붙여 URL을 구성합니다. 여기에 1.6b4 버전이 들어있는 브랜치 stable/1.6.x 를 지정하는 @stable/1.6.x 를 붙입니다. 그냥 hash를 붙여도 됩니다. 마지막으로 =Django-1.6b4를 붙여서 패키지 이름과 버전을 지정합니다. 이 부분이 앞서 install_requires에 지정된 부분과 일치해야 합니다.

이 방법을 소개하는 이유는, 같은 방법으로 여러분의 회사가 사용하는 git 저장소도 지정할 수 있기 때문입니다. 모든 패키지를 PyPI에 올려놓을 수 없는 분들은 이 방법을 사용하면 됩니다.

이 설정은 1.6b4 이다음 버전이 나오면 아마도 동작하지 않게 될 것입니다. 그리고 곧 다음 버전이 나올 것으로 기대됩니다. 이 구성은 단지 예시를 위한 것입니다. URL과 버전을 적당히 고쳐서 대응하는 것은 독자의 몫으로 남겨둡니다.

콘솔 스크립트

Setuptools는 플랫폼 별로 콘솔 스크립트를 설치할 수 있습니다. entry_points 옵션에 console_scripts 키를 사용하여 지정하면 되는데, 이 예에서는 세 개의 콘솔 스크립트 publish, scan, update를 설치하는데, 셋 모두 flowdas.books.script 모듈의 main 함수가 호출되게 됩니다. 이 경우는 main 함수가 어떤 명령이 호출되었는지 판단하도록 구성한 것인데, 보통은 각 콘솔 스크립트 별로 별도의 모듈이나 함수를 지정합니다.

장고에서는 애플리케이션이 manage.py의 명령어를 추가하도록 지원합니다. 위의 세 명령어도 이런 방식으로 추가된 manage.py의 명령어입니다. flowdas.books.script 모듈의 main 함수는 manage.py를 실행하도록 구성된 것입니다. flowdas.books.script 모듈은 이렇게 구성됩니다.

 

 

 

https://stackoverflow.com/questions/58533084/what-keyword-arguments-does-setuptools-setup-accept

 

What keyword arguments does setuptools.setup() accept?

What is the full list of keyword arguments that the setuptools.setup() function accepts? (Please include a description of what each argument means, if possible.) I have looked all over the web an...

stackoverflow.com

 

 

https://edykim.com/ko/post/how-does-setup.py-differ-from-requirements.txt-and-how-to-use-it/

 

setup.py와 requirements.txt의 차이점과 사용 방법

파이썬을 사용하다보면 setup.py와 requirements.txt…

edykim.com

 

728x90