pipの依存関係チェックが厳しくなる

# はじめに

これも[Python Bytesポッドキャスト](https://pythonbytes.fm/)を聴いていて知ったのですが、pipがモジュールインストール時に行う依存性チェックが厳しくなるみたいです。気にしないでいるとある日突然 pip をバージョンアップした途端に依存しているパッケージをインストールできなくなるかも知れないです。どう変わるのか、そしてそれに備えて何ができるのかを書いておこうかと思います。

# pipの新しい依存関係チェッカー

あまり意識していませんでしが、これまでのpipでは依存関係に矛盾のあるパッケージをインストールできてしまっていました。pipの新しい依存性チェッカー (2020-resolver)ではこれを許さなくなります。

例えば、`virtualenv`パッケージ(version 20.0.2)は`six>=1.12.0, <2`という依存関係が設定されています。「sixのv1.12.0以上かつv2未満」ということですが、意図的にその依存関係を壊す形でのインストールを試みることができます。

```python
pip install "six<1.12" "virtualenv==20.0.2"
```

これをpipのv20.0.1以前のバージョンで実行するとこのようになります。

```shell
$ pip install "six<1.12" "virtualenv==20.0.2"
pip install "six<1.12" virtualenv==20.0.2
Collecting six<1.12
  Using cached six-1.11.0-py2.py3-none-any.whl (10 kB)
Collecting virtualenv==20.0.2
  Using cached virtualenv-20.0.2-py2.py3-none-any.whl (4.6 MB)
Collecting distlib<1,>=0.3.0
  Using cached distlib-0.3.1-py2.py3-none-any.whl (335 kB)
Collecting appdirs<2,>=1.4.3
  Using cached appdirs-1.4.4-py2.py3-none-any.whl (9.6 kB)
Collecting filelock<4,>=3.0.0
  Using cached filelock-3.0.12-py3-none-any.whl (7.6 kB)
ERROR: virtualenv 20.0.2 has requirement six<2,>=1.12.0, but you'll have six 1.11.0 which is incompatible.
Installing collected packages: six, distlib, appdirs, filelock, virtualenv
Successfully installed appdirs-1.4.4 distlib-0.3.1 filelock-3.0.12 six-1.11.0 virtualenv-20.0.2

```

メッセージ中では ERROR と出ていますが、インストール自体はできてしまっています。見逃してしまう可能性がありますね。

これが v20.0.2からはこうなります。

```shell
 pip install  "six<1.12" virtualenv==20.0.2
Collecting six<1.12
  Using cached six-1.11.0-py2.py3-none-any.whl (10 kB)
Collecting virtualenv==20.0.2
  Using cached virtualenv-20.0.2-py2.py3-none-any.whl (4.6 MB)
Collecting appdirs<2,>=1.4.3
  Using cached appdirs-1.4.4-py2.py3-none-any.whl (9.6 kB)
Collecting distlib<1,>=0.3.0
  Using cached distlib-0.3.1-py2.py3-none-any.whl (335 kB)
Collecting filelock<4,>=3.0.0
  Using cached filelock-3.0.12-py3-none-any.whl (7.6 kB)
Installing collected packages: six, appdirs, distlib, filelock, virtualenv
ERROR: After October 2020 you may experience errors when installing or updating packages. This is because pip will change the way that it resolves dependency conflicts.

We recommend you use --use-feature=2020-resolver to test your packages with the new resolver before it becomes the default.

virtualenv 20.0.2 requires six<2,>=1.12.0, but you'll have six 1.11.0 which is incompatible.
```

エラーメッセージが変わっています。

* 2020年10月以降にpipの挙動が変わるのでパッケージのインストールやアップグレードのときにエラーになってしまうかも
* `--use-feature=2020-resolver`オプション付きで実行して、新しいリゾルバーがデフォルトになる前に試してみて。

ということで、そのオプション付きで実行してみます。

```shell
$ pip install --use-feature=2020-resolver "six<1.12" virtualenv==20.0.2
Collecting virtualenv==20.0.2
  Using cached virtualenv-20.0.2-py2.py3-none-any.whl (4.6 MB)
ERROR: Cannot install six<1.12 and virtualenv 20.0.2 because these package versions have conflicting dependencies.

The conflict is caused by:
    The user requested six<1.12
    virtualenv 20.0.2 depends on six<2 and >=1.12.0

To fix this you could try to:
1. loosen the range of package versions you've specified
2. remove package versions to allow pip attempt to solve the dependency conflict

ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/user_guide/#fixing-conflicting-dependencies
```

依存関係が満たせないのでインストールできなくなっています。これが将来のpipの動作になります。

# 導入のスケジュール

新しい依存関係チェッカーは以下のスケジュールで導入されます。

| バージョン | 導入時期   | 動作                                                         |
| ---------- | ---------- | ------------------------------------------------------------ |
| 20.2       | 導入済み   | デフォルトでは既存の依存関係チェッカー（リゾルバー）が使われますが、`--use-feature=2020-resolver`オプションで新しいリゾルバーを使えます。 |
| 20.3       | 2020年10月 | デフォルトで新しいリゾルバーを使うようになります。`--use-deprecated=legacy-resolver`オプションで以前のリゾルバーを使えます。 |
| 21.0       | 未定       | 新しいリゾルバーのみを使えます（古いリゾルバーは消去）       |

# 今、何をしたら良いのか

* pipを最新の 20.2にする
* `--use-feature=2020-resolver`オプション付きでpipを使う

これで将来のバージョンの動作を先取りできます。これで10月になって慌てなくて良くなりますが、特に `pip install -r requirements.txt`あるいは `install -c constraints.txt`を使ってバージョン指定している場合に効果があります。というのも、これまでのバージョンの pipで `pip freeze`して作った requirements.txt あるいは constraints.txt には依存関係が矛盾した状態で書き出されているかも知れず、それを事前にチェックできるということです。

一々オプション指定するのが面倒という方は、`~/.config/pip/pip.conf`に

```ini
[install]
use-feature=2020-resolver
```

と書いておくと自動でつけてくれます。将来、バージョンが上がったときに消すのを忘れないようにしないとですが。

# まとめ

pipの依存関係チェックの動作が変わるのでそれについて書いてみました。pythonのパッケージ管理は [Poetry](https://python-poetry.org/)とか[pipenv](https://pipenv-fork.readthedocs.io/en/latest/)とかありますが、本家のpipも独自に進化しているんですね。また気になる機能追加があったら書いてみたいと思います。