Python: poetryでパッケージの依存管理

私はこれまでPythonのパッケージ管理として pyenv + pipenv を主に使ってきました。が、最近はpipenvは色々あって使いづらさを感じていました。

  • pipenv lockpipenv syncが遅い (気がする)
  • pipenv自体の更新が怪しかった (参考、今年に入って4月と6月にリリースされている模様)

乗り換える程の理由でもないのですが、代替となるツールは探しておかないとなあとふんわり思っていた頃、同僚がpoetryを使っていて良さそうでしたので、使い方をまとめながら紹介したいと思います。

poetry

poetryは、主にパッケージ依存関係の解決・インストール・更新と仮想環境の構築を行ってくれるコマンドラインツールです。上の通りpipenvなどが競合するツールになります。

python-poetry.org

github.com

インストール

pipで入れてしまうのが一番手軽です。

$ python --version
Python 3.8.5

$ pip install poetry

プロジェクト作成

poetry newでpoetryプロジェクトの標準的なディレクトリ構成が作られます。

  • 既存のプロジェクトにpoetryで依存関係を管理する場合、newの代わりにpoetry initを使います。
$ poetry new poetry-example
Created package poetry_example in poetry-example

$ cd poetry-example
$ tree
.
├── README.rst
├── poetry_example
│   └── __init__.py
├── pyproject.toml
└── tests
    ├── __init__.py
    └── test_poetry_example.py

このとき作成されるpyproject.tomlに追加するパッケージを記述していきます。

[tool.poetry]
name = "poetry-example"
version = "0.1.0"
description = ""
authors = ["ohke <...>"]

[tool.poetry.dependencies]
python = "^3.8"

[tool.poetry.dev-dependencies]
pytest = "^5.2"

[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"

仮想環境の構築

poetry install または poetry update とします。そうすると仮想環境 (virtualenv) が構築され、パッケージがインストールされます。

  • デフォルトではdev-dependenciesのパッケージもインストールされます (--no-devオプションで除くこともできます)
$ poetry install
Creating virtualenv poetry-example-JGBVk9r8-py3.8 in /Users/ohke/Library/Caches/pypoetry/virtualenvs
Updating dependencies
Resolving dependencies... (0.2s)

Writing lock file


Package operations: 11 installs, 0 updates, 0 removals

  - Installing zipp (3.1.0)
  - Installing importlib-metadata (1.7.0)
  - Installing pyparsing (2.4.7)
  - Installing six (1.15.0)
  - Installing attrs (19.3.0)
  - Installing more-itertools (8.4.0)
  - Installing packaging (20.4)
  - Installing pluggy (0.13.1)
  - Installing py (1.9.0)
  - Installing wcwidth (0.2.5)
  - Installing pytest (5.4.3)
  - Installing poetry-example (0.1.0)

生成されるpoetry.lockファイルに依存パッケージが出力されます。このあたりはpyenvと同じですね。

$ tree
.
├── README.rst
├── poetry.lock  # ★
├── poetry_example
│   └── __init__.py
├── poetry_example.egg-info
│   ├── PKG-INFO
│   ├── SOURCES.txt
│   ├── dependency_links.txt
│   └── top_level.txt
├── pyproject.toml
└── tests
    ├── __init__.py
    └── test_poetry_example.py

デフォルトではユーザホーム以下のキャッシュディレクトリに作成されますが、poetry configで設定を書き換えることでカレントディレクトリに作ることもできます。以下では./.venvに作られています。

# 上で作った環境を削除
$ rm -rf /Users/ohke/Library/Caches/pypoetry/virtualenvs/poetry-example-JGBVk9r8-py3.8

$ poetry config --list
cache-dir = "/Users/ohke/Library/Caches/pypoetry"
virtualenvs.create = true
virtualenvs.in-project = false
virtualenvs.path = "{cache-dir}/virtualenvs"  # /Users/ohke/Library/Caches/pypoetry/virtualenvs

$ poetry config virtualenvs.in-project true

$ poetry install
Creating virtualenv poetry-example in /Users/ohke/dev/private/poetry-example/.venv
...

仮想環境上での実行

poetry runで仮想環境でPythonコードを実行できます。上でインストールした仮想環境のパッケージがパスに含まれていることが確認できます。

  • poetry shellで仮想環境上でシェルが立ち上がります
$ cat poetry_example/main.py
import sys
from pprint import pprint

if __name__ == "__main__":
    pprint(sys.path)

$ poetry run python poetry_example/main.py
['/Users/ohke/dev/private/poetry-example/poetry_example',
 ...
 '/Users/ohke/dev/private/poetry-example/.venv/lib/python3.8/site-packages',
 ...]

パッケージのインストール

パッケージを追加する場合、poetry addを使います。これでpyproject.tomlとpoetry.lockが更新されます。

  • pyproject.tomlを直接書き換えた場合は、poetry updateで poetry.lockの更新 + 仮想環境へのインストール を行います
  • パッケージをアンインストールする場合は、poetry remove
$ poetry add numpy
Using version ^1.19.1 for numpy

Updating dependencies
Resolving dependencies... (1.6s)

Writing lock file


Package operations: 1 install, 0 updates, 0 removals

  - Installing numpy (1.19.1)

まとめ

概ねpipenvと変わらない機能を有することは確認できました。lockやsyncがどれだけ早くなるのか次第なところはありますが、現在pipenvを使っているプロジェクトを移行するまでのメリットは無さそうです。

去年12月に1.0がリリースされて以来、現在までバージョンアップも頻繁に行われているので、新しく作るシステムに関してはpoetryを使っていくのがいいのかな、という所感でした。