::: IT인터넷 :::

pyproject.toml을 이용한 파이썬 패키징 (3) - 실제 적용하기

곰탱이푸우 2023. 9. 28. 08:20
지금까지 파이썬 프로젝트의 패키징은 setuptools의 setup.py를 사용하는 것이 일반적이었다.
 
setup.py를 사용한 파이썬 프로젝트 폴더 구성은 아래 문서를 참고한다.
setup.py를 사용한 파이썬 프로젝트의 예제 코드는 아래 문서를 참고한다.
그러나 현재는 pyproject.toml의 사용이 권장되고, setup.py 사용을 자제하는 것이 권고되고 있다.
따라서 기존 setup.py를 pyproject.toml로 변경해야 하며, 빌드와 배포에서도 변경이 필요하다.
 
pyproject.toml을 실제 코드에 적용하는 방법을 정리한다.
 
setup.py의 문제에 대해서는 아래 문서를 참고한다.
pyproject.toml에 대한 소개는 아래 문서를 참고한다.
 

pyproject.toml로 변경하기

기존에 작성했던 setup.py를 pyproject.toml로 변경하고 빌드 스크립트에 반영하는 방법을 정리한다.
 
pyproject.toml의 스펙을 설명하는 것은 큰 의미가 없으므로, 세부 내용은 공식 문서를 참고한다.
코드 영역에서 수정해야 하는 부분과 빌드 스크립트에서 변경해야 하는 부분을 나눠서 정리한다.
 

코드 영역 수정

기존에 작성했던 프로젝트의 파일 구조는 다음과 같다.
여기서 코드를 수정해야 하는 부분은 크게 다음과 같다.
  • setup.py - pyproject.toml 로 수정 (setup.py 삭제)
  • info.py - VERSION, DEPLOY 파일로 분리 (info.py 삭제)
 
 

setup.py 를 pyproject.toml로 수정

pyproject.toml은 다음과 같이 작성하고, 기존의 setup.py 파일은 삭제한다.
자세한 내용은 주석을 참고한다.
# 빌드 시스템 (빌드 백엔드) 지정
[build-system]
requires = ["setuptools", "setuptools-scm"]
build-backend = "setuptools.build_meta"

# 프로젝트 정보 작성
[project]
name = "pyloncli"    # 패키지 이름
description = "Command Line Interface for Pylon Project"    # 패키지 간단 설명
readme = "README.md"    # README.md 파일 경로
requires-python = ">=3.8"    # 파이썬 최소 버전
classifiers = [
    'Environment :: Console',    # 실행환경 (GUI or Console)
    'Operating System :: POSIX :: LINUX',    # 실행환경 (OS)
    'Programming Language :: Python :: 3.8'    # 프로그래밍 언어와 버전
]
dynamic = ["version", "dependencies"]    # 동적 메타데이터 항목 정의

[tool.setuptools.dynamic]    # 동적 메타데이터 상세 정의
version = {file = ["VERSION"]}    # VERSION 파일에서 가져옴
dependencies = {file = ["requirements.txt"]}    # requirements.txt에서 가져옴

[tool.setuptools.packages.find]    # 패키지에 포함시킬 파일 경로
where = ["."]    # pyproject.toml이 위치한 경로
include = ["pyloncli*"]    # 하위의 pyloncli 경로는 포함
exclude = ["tests"]    # 하위의 tests 경로는 제외

[tool.setuptools.package-data]
"*" = ["*.yml"]    # 설정 파일 (yml) 포함

[project.scripts]    # 명령어로 실행하면 호출할 함수 정의 (entry_points와 동일)
pyloncli="pyloncli.main:main"    # pyloncli 명령어는 pyloncli.main.py의 main 함수 호출

[tool.pytest.ini_options]    # pytest 설정 (pytest.ini의 추가 설정)
pythonpath = ["."]    # sys.path에 추가할 경로 (테스트할 모듈 기본 경로)
testpaths = ["tests"]    # 테스트 코드 경로
addopts = ["--import-mode=importlib"]    # 테스트 코드를 import 하는 방식 정의
 
pytest의 ini_options에서 addopts 항목에 import-mode를 imporlib로 지정했다.
테스트 파일을 수집하기 위한 방법을 지정하는데, 3가지 방법이 있다.
구분
내용
단점
prepend (default)
테스트 파일을 실제로 import 하고 실행
모든 코드가 실행되어 오버헤드 발생
append
prepend와 동일하지만 마지막에 실행하고 추가 수집
모든 코드가 실행되어 오버헤드 발생
importlib
실행 없이 import 구문만 해석하여 수집
동적으로 생성하는 코드는 수집하지 못함
 
테스트 코드에서 동적으로 생성하는 부분이 없으므로 importlib로 지정하였다.
자세한 사항은 아래 문서를 참고한다.
 

info.py 파일의 분리

기존에는 빌드에 필요한 부가 정보들을 info.py로 정의해서 사용했다.
setup.py 에서 사용할 변수와 빌드 스크립트에서 사용할 환경 변수가 함께 정의되어 있는 형태이다.
 
# setup.py에서 사용할 패키지 이름과 버전 정의
__package_name__="pyloncli"
__version__="1.0.0.dev"
# 빌드 스크립트에서 사용할 환경 변수 정의
__private_deploy__=True
 
패키지 이름은 pyproject.toml에서 직접 정의하므로 제외한다.
 
__version__ 항목은 별도의 VERSION 파일로 분리하고 버전 값만 작성한다.
패키지 이름은 변경할 일이 많지 않지만, 버전 정보는 기능이 추가되거나 수정될 때마다 변경된다.
따라서 VERSION 파일을 별도로 관리하면 pyproject.toml 파일을 수정할 일이 줄어든다.
 
__private_deploy__ 항목도 별도의 DEPLOY 파일로 분리한다.
해당 값은 지정한 저장소 외에 추가적인 저장소에 배포 여부를 전달하기 위한 값으로 사용한다.
예를 들어 서버가 폐쇄망과 인터넷망에 각각 존재하고, 인터넷망 서버가 폐쇄망 서버에 접근하지 못하는 경우가 있다.
이러한 경우 인터넷망 서버에 저장소를 추가로 운영하게 되는데, 해당 저장소에 배포 여부를 결정할때 사용하는 값이다.
 
젠킨스에서 사용하는 빌드스크립트에서 해당 파일 내용을 환경변수로 등록하고 동작한다.
따라서 해당 파일은 별도의 DEPLOY 파일로 분리해서 관리하는 것이 좋다.
 
VERSION 파일과 DEPLOY 파일은 프로젝트 최상위 경로에 생성한다. pyproject.toml과 동일한 위치이다.
기존의 info.py 파일은 삭제한다.
 
VERSION 파일은 아래와 같이 작성한다.
1.0.0.dev
 
DEPLOY 파일은 다음과 같이 작성한다.
__private_deploy__=True
 

빌드 영역 수정

코드 영역 수정이 완료되면 기존의 빌드 스크립트를 수정한다.
 
기존에 사용하던 파이썬 빌드 스크립트 작성 방법은 아래 문서를 참고한다.
아래 항목이 적용되어야 한다.
  • 가상 환경을 생성하고 build, twine 라이브러리 설치
  • python setup.py bdist_wheel upload를 build와 twine으로 분리

 

 

build와 twine 설치

테스트와 빌드를 위한 파이썬 가상환경을 생성하는 부분에 build, twine 설치 명령을 추가한다.
아래와 같이 가상 환경을 생성하고 test_requirements.txt를 설치하기 전에 build와 twine을 설치한다.
$ source /opt/anaconda3/etc/profile.d/conda.sh
$ conda create -n dist_py38 python=3.8 -y
$ conda activate dist_py38
$ pip install build twine setuptools-scm    # 추가한 명령어
$ pip install -r test_requirements.txt
 

빌드 스크립트 수정

기존 빌드와 배포 명령은 python 명령으로 setup.py를 실행하고, bdist_wheel과 upload를 인자로 전달했다.
핵심은 python setup.py 형태의 실행 명령을 제거하는 것이다.
따라서 아래와 같이 python -m build과 twine upload로 변경한다.
자세한 내용은 주석을 참고한다.
#!/bin/bash
echo "============ Test is Successed!!!!!!!!!============"
source /opt/anaconda3/etc/profile.d/conda.sh
conda activate dist_py38

# 기존 빌드 및 배포 명령어
# python setup.py bdist_wheel upload -r local
# -w는 whl만 생성, --no-isolation은 가상환경 미생성
python -m build -w --no-isolation   

# 기존 업로드 명령어
# python setup.py bdist_wheel upload -r local
# -r은 원격 저장소에 업로드, dist/*.whl는 업로드 대상 파일 경로
twine upload -r local dist/*.whl   

if [ $__private_deploy__ == True ]; then
  # whl 파일은 이미 생성되어 있으므로 build는 미실행

  # 기존 업로드 명령어
  # python setup.py bdist_wheel upload -r private
  twine upload -r private dist/*.whl
fi

conda deactivate
conda remove -n dist_py38 --all