Poetry で Python プロジェクトの開発環境を揃えてみた

Python は長らく v2 系と v3 系の混在とか、マイナーバージョンが違うだけでも動かなくなる仕様変更とか、requirements.txt にバージョン番号が書かれないため手元で再現不可能なプロジェクトとか、Python 本体のイマイチポイントと、レベルの低い利用者が氾濫していることに起因する諸問題が多すぎて、個人的には好きじゃない言語。インデントの強制という仕組みは面白いのに、全体的に何か動作が遅いし、インタプリタ言語だからなのか構文が簡単だからなのか知らないが、その場しのぎで書かれたスクリプトみたいな粗製乱造コードが大量に散見される印象。「僕の環境では動いてます」なんて情報じゃ何にも解決しねえんだよどうしてお前はどこまでも自分目線でしか物を言えねえんだ、他人が見たり書いたりする可能性があるコードなら再現性が確立されたプロジェクトを作らんかいこのボケ!

ということで、自分が Python を書く時は Pipenv を使って Python 本体のバージョンを固定したり依存パッケージのバージョンを明記したりしてきたのだが、今回は Poetry という別のツールを試してみる。コチラの方が高機能だそうだがどうだろう。

Poetry をインストールする

今回は WSL2 Ubuntu 20.04.4 LTS にて環境を構築していく。事前の環境はこんな感じ。

$ type python
-bash: type: python: 見つかりません

$ type python3
python3 は /usr/bin/python3 です
$ python3 -V
Python 3.8.10

$ type pip
pip は /usr/bin/pip です
$ pip -V
pip 20.0.2 from /usr/lib/python3/dist-packages/pip (python 3.8)

$ type pip3
pip3 は /usr/bin/pip3 です
$ pip3 -V
pip 20.0.2 from /usr/lib/python3/dist-packages/pip (python 3.8)

python (python2) はもう標準ではインストールされていないみたい。pippip3 と同じ実体を示しているが、コマンドを打つ時は python3 と明言してあげる方が良いな。

Poetry のインストールは以下のようにする。python ではなく python3 コマンドで実行している。

$ curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python3 -
Retrieving Poetry metadata

This installer is deprecated. Poetry versions installed using this script will not be able to use 'self update' command to upgrade to 1.2.0a1 or later. It is recommended to use https://install.python-poetry.org instead. Instructions are available at https://python-poetry.org/docs/#installation
# Welcome to Poetry!

This will download and install the latest version of Poetry,
a dependency and package manager for Python.

It will add the `poetry` command to Poetry's bin directory, located at:

$HOME/.poetry/bin

This path will then be added to your `PATH` environment variable by
modifying the profile files located at:

$HOME/.profile
$HOME/.bash_profile

You can uninstall at any time by executing this script with the --uninstall option,
and these changes will be reverted.

Installing version: 1.1.14
  - Downloading poetry-1.1.14-linux.tar.gz (97.91MB)

Poetry (1.1.14) is installed now. Great!

To get started you need Poetry's bin directory ($HOME/.poetry/bin) in your `PATH`
environment variable. Next time you log in this will be done
automatically.

To configure your current shell run `source $HOME/.poetry/env`

ということでインストール完了。~/.bashrc に以下を追記した。

# ~/.bashrc
export PATH="${PATH}:${HOME}/.poetry/bin"

Poetry のバージョンを確認してみる。

# その場で読み込み
$ source "${HOME}/.poetry/env"

$ poetry -V
Poetry version 1.1.14

コレで OK。

Poetry でプロジェクトを作ってみる

Poetry でプロジェクトを作ってみる。$ poetry new は、$ go mod init とか $ cargo new とか $ zig init-exe とかみたいに、雛形ファイルがいくつか生成される感じみたい。

$ Poetry プロジェクトを作る
$ poetry new practice-poetry-project
Created package practice_poetry_project in practice-poetry-project

# 以下のようにファイルが生成されている
$ tree ./practice-poetry-project/
./practice-poetry-project/
├── README.rst
├── pyproject.toml
├── practice_poetry_project
│   └── __init__.py
└── tests
    ├── __init__.py
    └── practice_poetry_project.py

2 directories, 5 files

プロジェクト構成は次の資料が参考になる。

時たま、Git のリポジトリ名までスネークケースにしないといけないと思い込んでる奴がいるけど、Git と Python って何の関係もないし、上述のとおり代表的なプラクティスではプロジェクト名や親ディレクトリはケバブケースにするのが普通で、Google なんかのリポジトリを見てもケバブケースは普通に登場している。Python の基礎知識も持たずに書いてるザコだと判断して見下していくのでよろしく。

ちなみに、既存ディレクトリ内で $ poetry init --no-interaction と書いても、pyproject.toml を出力してくれる。コチラは $ npm init -y に近いかな。ただし、$ poetry new でプロジェクトを構築すると pytest がセットでインストールされるが、$ poetry init では何もインストールされないのでその点は注意。プロジェクト構成などの雛形を Python 標準に合わせたいのであれば $ poetry new で始める方が良いかな。

Poetry でパッケージ管理する

例えば mecab-python3 をインストールしたいな、という場合は $ poetry add でインストールする。

$ poetry add mecab-python3
Using version ^1.0.5 for mecab-python3

Updating dependencies
Resolving dependencies... (6.0s)

Writing lock file

Package operations: 8 installs, 0 updates, 0 removals

  • Installing pyparsing (3.0.9)
  • Installing attrs (22.1.0)
  • Installing more-itertools (8.13.0)
  • Installing packaging (21.3)
  • Installing pluggy (0.13.1)
  • Installing py (1.11.0)
  • Installing wcwidth (0.2.5)
  • Installing pytest (5.4.3)

# pyproject.toml に追記される
$ cat ./pyproject.toml
[tool.poetry]
name = "practice-poetry-project"
version = "0.1.0"
description = ""
authors = ["Neos21 <neos21@gmail.com>"]

[tool.poetry.dependencies]
python = "^3.8"
mecab-python3 = "^1.0.5"  # ← ココ

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

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

npm における dependenciesdevDependencies と同様で、開発者のみ利用するツールをインストールする際は -D オプションを付けることで [tool.poetry.dev-dependencies] ブロックの方に記述される。

# mypy は型ヒントを入れる、flake8 は静的チェック、black は自動フォーマッタ、isort は import のソート用
$ poetry add mypy flake8 black isort -D
Using version ^0.971 for mypy
Using version ^5.0.4 for flake8
Using version ^22.6.0 for black
Using version ^5.10.1 for isort

Updating dependencies
Resolving dependencies... (2.6s)

Writing lock file

Package operations: 13 installs, 0 updates, 0 removals

  • Installing click (8.1.3)
  • Installing mccabe (0.7.0)
  • Installing mypy-extensions (0.4.3)
  • Installing pathspec (0.9.0)
  • Installing platformdirs (2.5.2)
  • Installing pycodestyle (2.9.1)
  • Installing pyflakes (2.5.0)
  • Installing tomli (2.0.1)
  • Installing typing-extensions (4.3.0)
  • Installing black (22.6.0)
  • Installing flake8 (5.0.4)
  • Installing isort (5.10.1)
  • Installing mypy (0.971)

black については pyproject.toml[tool.black] というブロックを書けば、設定ファイルを増やさずに black 用の設定が書ける。flake8 については pyproject-flake8 というラッパーを使えば可能といわれているが、自分が試したところエラーが出てしまい正常に動作しなかった。ラッパー自体がまだアルファ版なので、素直に .flake8 設定ファイルを別個で作ってやることにした。

依存パッケージをインストールすると poetry.lock というファイルができるが、コレは公式がいうには Git 管理に含めるべき、だそうだ。pyproject.toml にはデフォルトではキャレット ^ 記号でバージョン番号が記載されるのでそれでも大抵の場合は問題なさそうだが、より厳密に環境情報を残せるだろう。

Poetry のちょっと残念なところは、Pipenv の [scripts] ブロック (npm でいう package.jsonscripts) 相当のスクリプトを書ける機能がないところ。[tool.poetry.scripts] というブロックは用途が異なるのだ。その点だけ注意。

Poetry で作られた仮想環境で作業する

Poetry も Pipenv と同じく、$ poetry shell と書けば指定の Python バージョンでの仮想環境が立ち上がって作業できるようになる。その他、$ poetry run を使えばシェルを起動することなく実行することもできる。

$ poetry shell
Spawning shell within /home/neo/.cache/pypoetry/virtualenvs/practice-poetry-project-NzgxAWMQ-py3.8
. /home/neo/.cache/pypoetry/virtualenvs/practice-poetry-project-NzgxAWMQ-py3.8/bin/activate

$$ type python3
python3 は /home/neo/.cache/pypoetry/virtualenvs/practice-poetry-project-NzgxAWMQ-py3.8/bin/python3 です

# 抜ける時は普通に exit
$$ exit

ちなみに python3 ではなく python コマンドも仮想環境の Python を指すように設定されている。

# 自分が作ったモジュールを実行するならこんな感じ
$ poetry shell
$$ python -m practice_poetry_project

# もしくは以下のように一発で実行
$ poetry run python -m practice_poetry_project

# 開発系のツールを実行する際も次のようにやれば良い
$ poetry run python -m mypy ./
$ poetry run python -m flake8 ./
$ poetry run python -m black ./
$ poetry run python -m isort ./
$ poetry run python -m pytest

こんな感じ。

なお、git clone などしてきた Poetry プロジェクトで環境構築する際は $ poetry install だけ最初に打てば良い。

Pipenv は Pipfile という設定ファイルを作るが、Poetry が作る pyproject.tomlPEP 518 で規定されている Python 標準の設定ファイル形式だ。今後はコチラに揃えておくと良いだろう。