2020 年の Python パッケージ管理ベストプラクティス

この記事は [Python Advent Calendar 2019](https://qiita.com/advent-calendar/2019/python) の 19 日目の記事です。

## 🐍 あらすじ

Python のパッケージ管理。特にここ数年で新しいツールが多く出たこともあり、一体何を使うべきなのか、少し調べただけでは分からないと思います。本記事では、新しめの管理ツールを独断と偏見で比較します。著者は Poetry 信者なのでバイアスが掛かっているので悪しからず。

- 本記事で書いていること
  - Pipenv、Poetry、Pyflow の違いと使い方
- 本記事で書いていないこと
  - Pyenv、Venv、Virtualenv などの既存ツールの説明

著者の環境は以下の通り。

- `Ubuntu 18.04`
- `Python 3.8.0`
- `Pipenv 2018.11.26`
- `Poetry 1.0.0`
- `Pyflow 0.2.1`

特に Poetry と Pyflow は開発途中なので、本記事の内容と違う可能性があるのでご了承ください。

ちなみに 2019/12/15 時点での GitHub の Star の推移はこんな感じ。少しタイミングが悪いのは、Poetry 1.0.0 のリリースがつい 3 日前だってこと。

[![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506317/62c904dc-be8f-14f8-9ce2-9a7353b65038.png)](https://codetabs.com/github-stars/github-star-history.html)

## 📝 紹介

まずはツールごとに簡単に紹介します。

### Pipenv

[![](https://img.shields.io/github/stars/pypa/pipenv?style=for-the-badge)](https://github.com/pypa/pipenv)

かなりの期間、`requirements.txt` でパッケージの記述していた Python のパッケージ管理 (というかライブラリ一覧を記述するだけ) の風潮を、Node.js の npm や yarn、Ruby の gem のように、依存関係も扱えるようにしたことで話題になったツールです。`Pipfile` というパッケージを管理するファイルと `Pipfile.lock` という依存関係が記述されるファイルを使います。依存関係を扱えるということは、例えば内部で numpy を使う pandas をインストールした環境で pandas をアンインストールすると、Pip では pandas のみが削除されるのに対して、Pipenv では numpy も同時に削除できます (他に依存しているライブラリが無ければ)。

```toml:Pipfile
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]
black = "*"

[packages]
numpy = "*"

[requires]
python_version = "3.8"

[pipenv]
allow_prereleases = true
```

### Poetry

[![](https://img.shields.io/github/stars/sdispater/poetry?style=for-the-badge)](https://github.com/python-poetry/poetry)

Pipenv が流行ると思われたのもつかの間、[PEP 518](https://www.python.org/dev/peps/pep-0518) で提案された `pyproject.toml` によるパッケージ管理を導入した Poetry が開発されました。Pipenv が alt-requirements.txt に過ぎないのに対し、Poetry はこれまでパッケージングする際に記述していた `setup.py` や `setup.cfg`、`MANIFEST.in` などのファイルもコンパクトに `pyproject.toml` に記述できる点で優れています。他にも、linter や formatter の設定を同じファイルに記述できます。

```toml:pyproject.toml
[tool.poetry]
name = "sample-ploject"
version = "1.0.0"
description = ""
authors = ["Your Name <you@example.com>"]
license = "MIT"

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

[tool.poetry.dev-dependencies]
black = {version = "^19.10b0", allow-prereleases = true}

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

### Pyflow

[![](https://img.shields.io/github/stars/David-OConnor/pyflow?style=for-the-badge)](https://github.com/David-OConnor/pyflow)

Pyflow はおそらく一番新参のパッケージ管理ツールです。Rust で書かれており、Poetry で導入された PEP 518 に加え、[PEP 582](https://www.python.org/dev/peps/pep-0582) で提案された、プロジェクト内で扱える仮想環境を複数の Python バージョンに対応させることができます。Pyenv + venv で 1 つの Python のパージョンの 1 つの仮想環境を扱う Pipenv/Poetry に対して、Pyflow は単体で Python のバージョンを複数管理して任意のバージョンで仮想環境を作ることができます。現環境で大きな恩恵は無い気もしますが、今後 Python にメジャーアップデートがあった場合などに重宝されるかもしれません。個人的な懸念は Rust で書かれていることで、速度等で恩恵がありそうな一方で、Rust にある程度の理解がないとエラーメッセージに対応しづらい点と、開発コミュニティが伸びにくいということです。

```toml:pyproject.toml
[tool.pyflow]
name = "sample-project"
py_version = "3.8"
version = "1.0.0"
authors = ["Your Name <you@example.com>"]

[tool.pyflow.scripts]

[tool.pyflow.dependencies]
numpy = "^1.17.4"

[tool.pyflow.dev-dependencies]
black = "^19.10.0b0"
```

## ✏️ 使い方

インストールはそれぞれのリンクを読んでいただきたいですが、基本的に Pip でユーザディレクトリにインストールするのがいいかと思います。ただ Pyflow は今のところ Pip を推奨しておらず、Mac では非対応らしいので Rust を入れてインストールするといいかもしれません。

- [pipenv#installation](https://github.com/pypa/pipenv#installation)
- [poetry#installation](https://github.com/python-poetry/poetry#installation)
- [pyflow#installation](https://github.com/David-OConnor/pyflow#installation)

全てのツールが CLI を内蔵しており、プロジェクトの作成、パッケージの追加や削除、実行などが可能です。それぞれコマンドが微妙に違うので、よく使うものだけ表にして比較してみます。

| 動作                            | Pip                                     | Pipenv                      | Poetry                      | Pyflow                   |
| ------------------------------- | --------------------------------------- | --------------------------- | --------------------------- | ------------------------ |
| プロジェクトの作成              | -                                       | -                           | `poetry new sample`         | `pyflow new sample`      |
| プロジェクトの初期化            | -                                       | `pipenv --python 3.8`       | `poetry init`               | `pyflow init`            |
| パッケージの追加                | `pip install numpy`                     | `pipenv install numpy`      | `poetry add numpy`          | `pyflow install numpy`   |
| パッケージの削除                | `pip uninstall numpy`                   | `pipenv uninstall numpy`    | `poetry remove numpy`       | `pyflow uninstall numpy` |
| 依存環境のインストール          | `pip install -r requirements.txt`       | `pipenv sync`               | `poetry install`            | `pyflow sync`            |
| 仮想環境内で実行                | -                                       | `pipenv run python main.py` | `poetry run python main.py` | `pyflow main.py`         |
| パッケージのビルド              | `python setup.py bdist_wheel`           | -                           | `poetry build`              | `pyflow package`         |
| パッケージのアップロード (PyPI) | `twine upload --repository pypi dist/*` | -                           | `poetry publish`            | `pyflow publish`         |

どれもコマンドは直感的ですが、Pyflow は Pipenv を意識しているみたいです。パッケージング周りで Poetry と Pyflow の良さが分かると思います。

Pyflow のプロジェクトページでは[特徴を比較した表](https://github.com/David-OConnor/pyflow#a-thoroughly-biased-feature-table)が載っていますので合わせて参考に。

それぞれの初期化後のディレクトリ構造は以下の通り (.lock ファイル生成のために numpy をインストールした後)。

```text:pipenv-tree
./
├── .venv/
├── Pipfile
└── Pipfile.lock
```

```text:poetry-tree
./
├── .venv/
├── poetry.lock
├── pyproject.toml
├── README.rst
├── sample/
└── tests/
```

```text:pyflow-tree
./
├── .git/
├── .gitignore
├── __pypackages__/
├── LICENSE
├── pyflow.lock
├── pyproject.toml
├── README.md
└── sample/
```

## 🤔 独断と偏見による評価

頑張って褒めます。

### Pipenv

最近使っていないので著者の勘違いだったら教えてください。

👍 1. Python パッケージ管理の歴史を変えたパイオニア

ずっと `requirements.txt` や conda[^1] で曖昧に管理されていた歴史を変えてくれたことは非常に大きな功績だと思います。最近記事に取り上げられることも増え、使い方も多く紹介されています。

[^1]: 著者は宗教上の理由で Anaconda を使うことが出来ません。

👍 2. 移行が楽

`requirements.txt` から引き継ぎが出来るように実装されているので、既存プロジェクトへの導入は比較的簡単です。

👎 1. `--pre` がライブラリごとに設定できない

alpha や beta 段階でしか公開されていないパッケージをインストールする際に、Pipenv では `--pre` オプションをつけるのですが、一度 `--pre` すると次回以降どのパッケージでも最近版を取ってきます。`black` をフォーマッタにしている場合、まだ正式版がリリースされていないので多分ハマります。

👎 2. パッケージのインストールが遅い

体感ですが、Pipenv はインストールがかなりと遅いときがあります。`torch` とか重た目のフレームワーク入れようとすると結構時間がかかる印象。計測するのが面倒なので誰か実験してみてください。

### Poetry

👍 1. 安定感がある

ここ半年はほぼ Poetry しか使っていませんが、エラーで困ることも機能で不足を感じることもありませんでした。パッケージングが楽な点、インタラクティブな CLI でプロジェクトを作成できる点など、npm と同じ感覚で使えています。

### Pyflow

👍 1. PEP 582 への対応

先日リリースされた `Python 3.8` から導入された PEP 582 にいち早く対応した点で評価できます。バージョンのスイッチや選択もコマンドラインでインタラクティブに行うことが出来ます。

👍 2. 仮想環境内での実行が楽

仮想環境内で実行する際に、`pyflow main.py` や `pyflow black` のように `run python` などをつけなくても引数から判定してくれるのでコマンドが短く済むのはいいと思いました。

👍 3. 移行が楽

`requirements.txt` だけでなく、`Pipfile` からの移植にも対応している点は評価できます。

👎 1. Rust への理解が必要

著者が今回 Pyflow を触ってみた感じですが、そこそこの頻度でエラーが出てよく分からなくなりました。現状では、Rust について最低限知っておく必要があるかもしれません。

👎 2. プロジェクト生成時のモジュール名

~~細かいことですが、Poetry は `poetry new` でハイフン付きのプロジェクトを作成した際に、ハイフンをアンダースコアに置換してモジュールディレクトリを生成してくれますが、Pyflow は `pyflow new` では同じ名前のモジュールディレクトリが生成されます。命名規則として PEP では短い単語を推奨していますが、アンダースコアの使用は認められています。現状 Pyflow ではこのような場合はディレクトリ名を手動で変える必要があります。~~

コントリビューターの方が修正してプルリク [#41](https://github.com/David-OConnor/pyflow/pull/41) を送ってくださいました。今はマージされて修正されています。

## 😎 オススメの使い方

これまで色々比較をして来ましたが、冒頭に述べたように今の所 Poetry を推しています。Poetry を普段使っている私がオススメする使い方を紹介します。

### Pyenv + Poetry

Poetry で仮想環境の管理を行うことが出来ますが、様々な Python プロジェクトを扱うと、様々な Python のバージョンが必要になると思います。Pyenv は任意のバージョンの Python をインストールすることが出来るのでオススメです。ホームディレクトリにインストールするので root がいらない点でも扱いやすいです。

### プロジェクト内に venv を作成

Pipenv と Poetry では、初期設定では venv がホームディレクトリ内に作られます。

Pipenv の場合は `export PIPENV_VENV_IN_PROJECT=1` を Poetry の場合は `poetry config virtualenvs.in-project true` を行うことで、プロジェクト内に仮想環境を作成できます。プロジェクト外にあると管理が不便なのでこれをオススメします。

### オススメの alias

```sh:.zshrc
alias po='poetry run'
alias pp='poetry run python'

function pdev () {
  poetry add -D black flake8 mypy pytest
}
```

Pipenv にしろ Poetry にしろ、venv のように `source .venv/bin/activate` する必要はありません (一応 `pipenv shell` のように環境に入るコマンドも存在)。理由としては、別のプロジェクトにターミナルの同じセッションで移動したときに `deactivate` し忘れて環境を変えてしまうことへの対処です。

## 📑 まとめ

Pyflow はさておいて、ところどころで比較されるようになった Pipenv vs. Poetry は少なくとも現状の機能面では Poetry に軍配が上がるでしょう。まだ `requirements.txt` や `setup.py` が使われているプロジェクトがほとんどですが、今後 `pyproject.toml` による管理がどんどん広まってくれたらと思います。是非小さなプロジェクトから導入してみてください。