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

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

728x90

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

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

 

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

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