はじめに
虹ヶ咲7th NEW TOKIMEKI LAND、楽しかったー! nikkieです。
uvでライブラリとして環境構築(uv init --lib
)したPythonプロジェクトについて、Dockerイメージをビルドする記事を再び更新します。
このたびuvのドキュメントに--no-editable
を見つけました!
目次
- はじめに
- 目次
- おさらい:uvを使ったプロジェクトでのDockerイメージビルド
- uv syncが--no-editableをサポート!
- 結論:--no-editableを使ったDockerfile
- 終わりに
おさらい:uvを使ったプロジェクトでのDockerイメージビルド
uv 0.4.0から、プロジェクトをアプリケーション(--app
)またはライブラリ(--lib
)と区別してuv init
できるようになりました
それぞれでDockerfileを提案しています。
- アプリケーション
- ライブラリ
今回はライブラリのDockerfileのアップデートです。
これまでは以下のようにしていました。
- 自作ライブラリに必要な依存だけをまずインストール
uv sync --no-install-project
- 自作ライブラリ自体のインストール
- ここでeditable installをしない(=site-packages下に自作ライブラリを置く1)
uv build
uv pip install
- ここでeditable installをしない(=site-packages下に自作ライブラリを置く1)
このたびuv sync --no-editable
に気づいたことで、上記手順が非常に簡単になります!
uv sync
が--no-editable
をサポート!
uvのドキュメントのIntegration guideにはDockerに関するページがあります。
そこに「Non-editable installs」があることに気づきました2。
- uvはデフォルトでプロジェクトをeditable install
- 通常のinstall(=editableでないinstall)はプロジェクトのコードをsite-packagesにコピーする。プロジェクトのコードをいじったら再度installが必要
- editable installを一度しておくと、その後プロジェクトのコードをいじっても再インストール不要です。開発中は重宝します
- 一方、ビルドしたDockerイメージにおいては、イメージ内のコードを変えることはないのでeditable installの必要性はありません
- さらに言うと、マルチステージビルドでsite-packagesをコピーしたいので、editable installでない方が嬉しいです
uv sync
に--no-editable
を指定すると、通常のインストールをする3
--no-editable
の利用シーンとして、マルチステージビルドが挙げられています。
In the context of a multi-stage Docker image,
--no-editable
can be used to include the project in the synced virtual environment from one stage, then copy the virtual environment alone (and not the source code) into the final image.
そうなんですよ!(だから私は提案するブログを書いてきたわけなんです)
感動したのですが、uvのドキュメントなのにマルチステージビルドの例はuvを残さないんですよね。
これは私のこれまでの主張「Dockerイメージで提供するアプリケーションのユーザにはuvは価値を届けないので、Dockerイメージには不要」と一致します。
uv、お前、分かってんじゃん!!👍
--no-editable
がサポートされたのは、uv 0.4.11からでした。
https://github.com/astral-sh/uv/releases/tag/0.4.11
私はuv 0.4.9でDockerイメージの作り方を考えていた4ため、これまでの執筆では気づいていませんでした。
結論:--no-editableを使ったDockerfile
プロジェクト構造は以下です。
mylib/ ├── .venv/ ├── .python-version ├── src/ │ └── mylib/ │ ├── __init__.py │ ├── __main__.py │ └── py.typed ├── pyproject.toml └── uv.lock
Dockerfileはこのようになりました!
% docker run --rm uv-practice-lib:0.3.0 Hello from mylib! ['kokoro', 'aki', 'fuka', 'rion', 'subaru', 'masamune', 'ureshino'] % docker run --rm -it uv-practice-lib:0.3.0 bash root@128a492163e2:/# ls /usr/local/lib/python3.12/site-packages/ # mylibのインストールを確認 README.txt kojo_fan_art-0.1.1.dist-info pip-24.2.dist-info __pycache__ mylib the_solitary_castle_in_the_mirror _virtualenv.pth mylib-0.1.0.dist-info _virtualenv.py pip
Dockerfileの差分はこちら
RUN --mount=type=cache,target=/root/.cache/uv \ - uv sync --frozen --no-dev --no-install-project -RUN <<INSTALL_PROJECT - uv build - uv pip install --no-deps dist/mylib-*.whl -INSTALL_PROJECT + uv sync --frozen --no-dev --no-editable
before:
自作ライブラリに必要な依存だけインストール -> 自作ライブラリ自体のeditableでないインストール
after:
uv sync --no-editable
で自作ライブラリ自体のeditableでないインストール & 自作ライブラリに必要な依存のインストール
1コマンドで済んで、簡潔になりました5🙌
実装してくださってありがとうございます!
ちなみに
--frozen
:uvはpyproject.tomlの記載に則り、uv.lock
を更新してからsyncします。Dockerイメージはuv.lock
を更新せずにビルドしたいので指定しています--no-dev
:開発でだけ使う依存(例:uv add --dev pytest
)をDockerイメージにインストールしないために指定します(uv同様、開発者にとってはとても便利ですが、アプリケーションのユーザには価値を届けませんからね)
ソースコードの全体はこちらをどうぞ
終わりに
uvで環境構築したPythonライブラリのDockerイメージについて、--no-editable
を使ってDockerfileを簡潔にすることができました。
uvのドキュメントにuvを含まないマルチステージビルドの例が載ったのは、私としては嬉しい限りです。
自分たちが作っているuvというツールよりも広い範囲をちゃんと見ているんだなと思いました。
--no-editable
が実装された背景は残念ながらissueを見てもよく分からない6のですが、欲しい機能だったので不問にしておきましょう7
- マルチステージビルドでsite-packagesをコピーするためです ↩
-
uv build
が壊れていた記事を書く中で気づきました ↩ -
uv run
も--no-editable
をサポートしたそうです↩ - uv 0.4.9 ↩
- 私はオッカムの剃刀を信奉しています ↩
- Do we want to support this? おそらくAstral社の内部のコミュニケーションが別にあるんだと思います↩
- uvについて私がもやっとするのは、他のPythonのツール(例:pip)と比べたときに機能追加の意思決定がオープンにされていない点です。なんか社内の内輪乗りのような。別のもやっととして、届いたpull requestを(なぜその機能が求められるのかという背景を考えずに)バシバシマージしているように見えます。これらは、uvという期待されているツールがちゃんと育っていけるのか不安を抱かせます↩