파이썬

파이썬 프로젝트 의존성 관리를 잘해보자

정한_s 2023. 8. 19. 13:52

conda 와 virtual env에서 requirement.txt를 추출하다 보니, 이전에 실험용으로 다운 받은 의존성도 실수로 같이 받아져서,  requirement.txt 가 커지고 빌드 시간이 나는 문제가 있었습니다.

또한 더 좋은 편의성을 갖추고 싶었습니다. 편의성 측면에서 원했던 것은 크게 2가지 였습니다. 첫번째는 모듈을 받을 때 어떤 모듈에 의존성을 가지는지 궁금했고, 의존성 트리를 보고 싶었습니다.  두번째는 test와 dev, local 등으로 group을 나누어 의존성을 관리하면 의존성을 보기 쉽고 안전하게 만들고 싶었습니다.

 

이러한 이유로 poetry를 도입하게 되었고, poetry에 대해서 간략한 설명과 어떻게 사용되고 있는지 공유하겠습니다. 

 

poetry란?

poetry는 파이썬 의존성 관리 툴입니다. poetry는 모든 패키지의 정확한 버전을 기록하고, 같은 프로젝트를 실행할 때도 동일한 환경을 구성할 수 있습니다.

(java의 maven이나 gradle, js의 npm과 비슷한 툴로 볼 수 있습니다.) 

poetry는 pyproject.toml 파일을 기반으로 의존성을 관리합니다. 

설치 방법 

https://python-poetry.org/docs/#installation

# Linux, macOS, Windows (WSL) 
curl -sSL https://install.python-poetry.org | python3 - 
# 버전확인 
poetry --version

poetry 적용 방법

poetry에서는 두가지 주요 파일을 사용합니다.pyproject.toml 파일과 poetry.lock 파일입니다. pyproject.toml 파일은 프로젝트의 구성과 종속성을 정의하고, poetry.lock은 해당 종속성의 구체적인 버전을 고정합니다. (추가로 poetry.toml 파일이 커스텀 환경 세팅을 위해 존재합니다)

 

새로운 프로젝트 ( poetry new )

poetry new my-package
// 이후로는 다음과 같이 폴더 구조가 만들어집니다
├── pyproject.toml
├── README.md
├── my_package
│ └── __init__.py
└── tests
└── __init__.py

기존 프로젝트 ( poetry init )

poetry init
// 해당 디렉터리 하위에 pyporject.toml가 만들어집니다

 

추가로 poetry local 환경 설정 방법이 있습니다. 기본적으로 설정하는 것이 아닌 커스텀한 환경 설정을 원할 때 poetry.toml 을 사용합니다. 관련 설정 url ( https://python-poetry.org/docs/pyproject/

 

poetry.toml 예시

[virtualenvs]
create = true
in-project = true
path = ".venv"

poetry 사용 방법


https://python-poetry.org/docs/cli/

 

add : pyproject.toml에 패키지 추가 하며, 패키지를 설치 합니다

poetry add <의존성 패키지>


install : 현재 pyproject.toml를 기반으로 의존성 설치를 진행합니다

poetry install // test dependency 그룹을 제외하고 설치 합니다. 
poetry install --without test 
// main 그룹과 dev 그룹을 설치 합니다 
poetry install --with dev
// dev 그룹만 설치 합니다 
poetry install --only dev

 

update : 의존성 패키지의 버전을 업데이트 합니다. 

poetry update// tip: poetry update 시에 pyporject.toml 기반으로 poetry.lock을 갱신하기 때문에 의존성 추가 삭제에도 update를 활용할 수 있습니다

 

remove: 의존성 패키지를 제거 합니다

poetry remove <의존성 패키지>

 

show : 의존성 구성이 어떻게 되어 있는 지 볼 수 있습니다

// 설치된 모든 패키지를 보여준다.
poetry show
 
// dev 제외하고 보여준다.
poetry show --without dev
 
# 특정패키지를 지정하면 상세내용을 보여줍니다.
poetry show django
 
# 최신 버전을 보여준다.
poetry show --latest (-l)
 
# 업데이트를 해야하는 패키지들을 보여준다.
poetry show --outdate (-o)
 
# 의존성 트리를 보여준다.
poetry show --tree

의존성 트리 예시

build : 소스를 배포가능한 형태로 빌드 합니다 
하위 디렉토리에 배포 가능한 파일이 생성됩니다.


check : toml 에러가 있는지 확인합니다

 

그렇다면 프로젝트에서 어떻게 사용되어 있을 까요?


디렉토리 구조

.
├── .coverage
├── .flake8 // flake8가 toml을 지원을 잘 안해주어 설정 파일을 따로 뺐습니다
├── .git
├── .gitignore
├── .gitlab
├── .gitlab-ci.yml
├── .idea // ide
├── .mypy_cache // mypy cache
├── .pre-commit-config.yaml // pre-commit 설정 파일 
├── .pytest_cache // pytest cache
├── Dockerfile // 도커 파일
├── README.md
├── src // 소스 파일
├── poetry.lock
├── pyproject.toml
├── requirements.txt // .lock을 기반으로 제작되는 requirements.txt
├── sonar-project.properties
└── tests // test 파일

 

poetry 구조

[tool.poetry]
name = ""
version = "0.1.0"
description = ""
authors = [""]
readme = "README.md"
packages = [{include = ""}]
 
[tool.poetry.dependencies]
python = "^3.10"
fastapi = {extras = ["all"], version = "^0.101.0"}
prometheus-fastapi-instrumentator = "^6.1.0"
requests = "^2.31.0"
aioredis = "^2.0.1"
aiohttp = "^3.8.5"
pytz = "^2023.3"
databases = {extras = ["aiomysql"], version = "^0.7.0"}
greenlet = "^2.0.2"
pycryptodome = "^3.18.0"
xmltodict = "^0.13.0"
 
[tool.poetry.group.test.dependencies]
pytest = "^7.4.0"
pytest-asyncio = "^0.21.1"
pytest-cov = "^4.1.0"
 
[tool.poetry.group.dev.dependencies]
setuptools = "^68.0.0"
black = "^23.7.0"
flake8 = "^6.1.0"
pre-commit = "^3.3.3"
mypy = "^1.4.1"
types-pytz = "^2023.3.0.0"
types-requests = "^2.31.0.2"
 
[tool.black]
line-length = 88
 
[tool.mypy]
ignore_missing_imports = true
 
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

해당 poetry 구조 이며, black과 mypy 등의 설정 정보가 담겨져 있습니다. 

해당 파일의 구체적인 의존성 설정은  다음 문서를 참고 하시면 될 것 같습니다. ( https://python-poetry.org/docs/dependency-specification/ )

 

 

추가로 Peotry는 Poetry 설치 중에 사용된 파이썬 버전에 따라 현재 프로젝트에 가상환경을 생성하려고 합니다.

만약 프로젝트의 python 버전이 맞지 않으면 오류가 나기 때문에 env use 라는 커맨드를 사용해서 프로젝트에서 사용할 파이썬 버전을 알려 줄 수 있습니다.

현재 Poetry는 사용자 환경에 있는 파이썬 버전을 선택할 수 있는 것이지, 선택한 파이썬 버전을 자동으로 설치해주지는 않습니다. 

따라서 파이썬 버전을 쉽게 관리할 수 있는 pyenv와 함께 사용하면 더욱 효과적입니다.

pyenv

mac brew 설치 방법

brew install pyenv ( pyenv 설치 )
 
# for zsh 설정
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc
 
 
# for ~/.bash_profile 설정echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(pyenv init -)"' >> ~/.bash_profile

with peorty

poetry.toml

[virtualenvs]
...
( + ) prefer-active-python = true (추가, 해당 옵션은 현재 활성화된 파이썬 버전을 사용하겠다는 것입니다 default=false)

poetry 가상환경 설정 예시 (python 버전이 3.9.8 일 경우)

pyenv install 3.9.8
pyenv local 3.9.8  # 현재 디렉토리에서 python 버전 3.9.8 쓴다는 의미입니다.
poetry install

주의!  만약 pyenv에 이미 프로젝트 기본 가상환경을 변경하고 싶다면, 해당 가상 환경을 지우고 install 해주세요!

가상 환경 지우는 방법

poetry env remove <가상환경>
poetry env remove --all (전체 삭제)

 

Advance


끝으로 현재 gitlab runner poetry 까지 설정을 하지 못해, requirement txt로 각 패키지를 설정하고 있는데요. 
그렇다면 requirement.txt를 poetry를 싱크를 맞추는 방법 보겠습니다.

poetry에서 공식문서에서 export 기능을 제공합니다. https://python-poetry.org/docs/cli#export
따라서 export를 활용하여 requirement를 갱신하는 방법이 있습니다. 
export 예시

poetry export -f requirements.txt --output requirements.txt


저희는 이거를 자동화 하기 위해서 
pre-commit을 이용하여 .lock 파일이 변경될 때마다 requirements.txt를 변경하고 있습니다. 

pre-commit 설치 

pip install pre-commit // or poetry add --group dev pre-commit


.pre-commit-config.yaml

# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
-   repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
    -   id: trailing-whitespace
    -   id: end-of-file-fixer
    -   id: check-yaml
    -   id: check-added-large-files
 
-   repo: https://github.com/python-poetry/poetry
    rev: 1.5.1  # add version here
    hooks:
    -   id: poetry-check
    -   id: poetry-lock
    -   id: poetry-export
        args: ["-f", "requirements.txt", "--with=test", "--without-hashes", "-o", "requirements.txt"] // main + test 를 합친 의존성을 해시 없이 requirements 추출