nikkie-ftnextの日記

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

uv 0.4.11からuv syncが --no-editable をサポート! 自作Pythonライブラリを含んだDockerイメージビルド手順を再改訂

はじめに

虹ヶ咲7th NEW TOKIMEKI LAND、楽しかったー! nikkieです。

uvでライブラリとして環境構築(uv init --lib)したPythonプロジェクトについて、Dockerイメージをビルドする記事を再び更新します。

このたびuvのドキュメントに--no-editableを見つけました!

目次

おさらい:uvを使ったプロジェクトでのDockerイメージビルド

uv 0.4.0から、プロジェクトをアプリケーション(--app)またはライブラリ(--lib)と区別してuv initできるようになりました

  • アプリケーションは、配布を想定していないPythonプロジェクト
  • ライブラリは、配布を想定しているPythonプロジェクト(他のプロジェクトでインストールできる)

それぞれでDockerfileを提案しています。

  • アプリケーション
  • ライブラリ

今回はライブラリのDockerfileのアップデートです。
これまでは以下のようにしていました。

  1. 自作ライブラリに必要な依存だけをまずインストール
    • uv sync --no-install-project
  2. 自作ライブラリ自体のインストール
    • ここでeditable installをしない(=site-packages下に自作ライブラリを置く1
      1. uv build
      2. uv pip install

このたび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


  1. マルチステージビルドでsite-packagesをコピーするためです
  2. uv buildが壊れていた記事を書く中で気づきました
  3. uv run--no-editableをサポートしたそうです
  4. uv 0.4.9
  5. 私はオッカムの剃刀を信奉しています
  6. Do we want to support this? おそらくAstral社の内部のコミュニケーションが別にあるんだと思います
  7. uvについて私がもやっとするのは、他のPythonのツール(例:pip)と比べたときに機能追加の意思決定がオープンにされていない点です。なんか社内の内輪乗りのような。別のもやっととして、届いたpull requestを(なぜその機能が求められるのかという背景を考えずに)バシバシマージしているように見えます。これらは、uvという期待されているツールがちゃんと育っていけるのか不安を抱かせます