nikkie-ftnextの日記

イベントレポートや読書メモを発信

モノレポでの開発なのですが、pip 20.3からの新リゾルバの挙動に悩まされています(1つの仮想環境に全部をeditable installできないよ〜)

はじめに

お助けプリーズ🙏 nikkieです

今さらの話ではあるのですが、「pipの動きが変わったなー」と思っていた事象がどうやら新リゾルバに起因するらしいとこのたび分かりました。
ラバーダッキングも狙って、何に困っているかを書き出してみます。

目次

pip 20.3からの新リゾル

遡ること約3年、Pythonのpipは依存解決の動きが変わったのです!

ばんくしさんのエントリがめちゃ詳しく、分かりやすかったです。
✍️強い依存解決を「しない」から「する」へ変わった

pipにおける重要な変更として、2つ目に、現在テスト段階にある依存解決を行うリゾルバ、2020-resolverのリリースがある。

先述の通り、pipは強い依存解決を行わないツールだった。(略) 対して2020-resolverは、シンプルなbacktrackingを利用した依存解決リゾルバであり、強い依存解決を事前に行う事で、pipのインストールの前後関係による課題を解消するものである。

英語での情報は以下

モノレポで大きな1つの仮想環境を作りたいときに困った!

サンプルモノレポ

複数のPythonライブラリを1つのリポジトリで開発する状況を指して、ここではモノレポという語を使いました。
説明のための例として、lib-a1とlib-bを1つのリポジトリで開発する場合です。

ここでlib-bはlib-aに依存します

libB/setup.py

setup(
    ...,
    install_requires=[
        "lib_a@git+ssh://git@github.com/ftnext/monorepo-python-practice@main#egg=lib_a&subdirectory=libA"
    ],
)

開発環境構築では、1つのvenvにlib-aもlib-bもeditable installできるといいんですよね〜。
editable installとは、Pythonファイルをコピーせずにimportパスに加えるインストール。
ref: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs

モノレポで1つのvenvにlib-aもlib-bも editable installできていると、lib-aを変更した後にインストール(=ファイルコピー)し直さなくても、lib-bにもlib-aの変更が反映されるのです。

.  # リポジトリのルートディレクトリ
├── libA  # lib-aの実装
├── libB  # lib-bの実装。lib-aに依存
└── venv  # この仮想環境にlib-aもlib-bもeditable installしたい

pip 20.2.4まで

lib-a -> lib-b の順でeditable installすると、2つともeditable installできます
モノレポのルートディレクトリで操作していきます2

% python -V
Python 3.10.9
% python -m venv venv
% source venv/bin/activate

% # プロンプトの(venv)は省略します
% pip install -U pip==20.2.4
% pip install -e libA  # libA以下のsetup.pyを参照し、lib-aをeditable install
% pip install -e libB  # lib-bをeditable install

% pip list
Package    Version Location
---------- ------- --------------------------------------------------
lib-a      0.0.1   /.../monorepo-python-practice/libA
lib-b      0.0.1   /.../monorepo-python-practice/libB
pip        20.2.4
setuptools 65.5.0

出力内容を確認すると、pip install -e libBのとき、libBのsetup.pyのinstall_requiresに書いたlib-aへの依存は「Requirement already satisfied」として扱われるんですね。
なので、lib-aはeditable installのまま残り、venvには2つのライブラリがeditable installされた状態となります3

pip 20.3以降

同じ手順を適用すると、lib-aがGitHubVCS)から再インストールされ、editable installではなくなります。
lib-bのみeditable installとなります(これが悩ましい〜😣)4

% deactivate

% python -m venv venv --clear
% source venv/bin/activate

% pip install -U pip==20.3
% pip install -e libA
% pip install -e libB

% pip list
Package    Version Location
---------- ------- --------------------------------------------------
lib-a      0.0.1
lib-b      0.0.1   /.../monorepo-python-practice/libB
pip        20.3
setuptools 65.5.0

出力を見るとpip install -e libBのとき、依存するlib-aについてeditable installはアンインストールされ、VCSからインストールしています。
これによりlib-aはeditable installされていない状態になります。
私はlib-aもeditableのままにしたいのです〜

再度pip install -e libA とすればやりたい状態にはなります(同じコマンドを2回実行しているのが微妙です。また3つとか5つになったときに現実的かというと、う〜ん...)

% pip install -e libA
% pip list
Package    Version Location
---------- ------- --------------------------------------------------
lib-a      0.0.1   /.../monorepo-python-practice/libA
lib-b      0.0.1   /.../monorepo-python-practice/libB
pip        20.3
setuptools 65.5.0

試行錯誤ログ

Workaroundとしては以下です。
ですがきれいに解消できていないので、他の方に積極的にオススメしづらいですね。

pip install--no-depsオプション

lib-bをeditable installするところでpip install -e libB --no-depsとします

https://pip.pypa.io/en/stable/cli/pip_install/#cmdoption-no-deps

Don’t install package dependencies.

lib-bが依存するパッケージ(ここではlib-a)をインストールしません。
これによりlib-aはeditable installのまま残ります。

% deactivate
% python -m venv venv --clear
% source venv/bin/activate

% pip install -U pip==20.3
% pip install -e libA
% pip install -e libB --no-deps
% pip list
Package    Version Location
---------- ------- --------------------------------------------------
lib-a      0.0.1   /.../monorepo-python-practice/libA
lib-b      0.0.1   /.../monorepo-python-practice/libB
pip        20.3
setuptools 65.5.0

lib-bがモノレポ外のサードパーティライブラリに依存した場合(例:requests)、requestsは別途pip installが必要となり、煩雑になっていくように思われます

pip install--ignore-installedオプション

https://pip.pypa.io/en/stable/cli/pip_install/#cmdoption-I

Ignore the installed packages, overwriting them.

% # 仮想環境の再作成。有効化は省略します

% pip install -U pip==20.3
% pip install -e libA
% pip install -e libB --ignore-installed
% pip list
Package    Version Location
---------- ------- -------------------------------------------------------------------------------
lib-a      0.0.1   /.../monorepo-python-practice/venv/lib/python3.10/site-packages
lib-b      0.0.1   /.../monorepo-python-practice/libB
pip        20.3
setuptools 65.5.0

lib-aはvenvの下にeditable installされました(GitHubなどのVCSからeditable installは可能です5)。
lib-aを変更する場合、libAの下だけでなくvenv下のソースもいじる必要があり、重複作業が気になりますね。

雑感

手を動かして感じたことの書き出しです。

終わりに

モノレポでの開発ですべてのライブラリを1つの仮想環境にeditable installしたいときに、pip 20.3以降の新リゾルバの挙動だとなかなか全部editable installにできなくて悩まされているという共有でした。
私の中では未解決問題です。
これはというアイデアが浮かんだ方、ぜひともお助けくだされ〜(@ftnextまでタレコミ待ってます!)

新リゾルバはなにか事情や妥当な理由があってeditable installをアンインストールするのだと思うので、その意思決定や実装は気になるところです。


  1. Pythonのパッケージ名ですが、正規化されるのでlib-aもlib_aも同じと今回知りました。ref: パッケージ名の正規化 — Python Packaging User Guide
  2. pyproject.tomlでの検証は宿題です。
  3. pip 20.2.4で--use-feature=2020-resolverを指定したところ、20.3以降の動きが再現しました。このことから新リゾルバの挙動に悩まされているという認識です
  4. 続く手順の--clearについて(rm不要なのでオススメです!)
  5. https://pip.pypa.io/en/stable/topics/vcs-support/#editable-vcs-installs