- 小さなAPIテストスクリプトでも依存関係のために実行環境をそろえる必要がありますが、uv実行shebangを使えば、インストール手順なしですぐ実行できる形にできます
- 例の
jam_users.py は、Go APIの /users エンドポイントをテストするために httpx、IPython、loguru を使い、ユーザーの削除・作成後にREPLへ入ります
- 従来の方法では、システムPythonにパッケージをグローバルインストールするか、仮想環境を自分で用意する必要があり、スクリプトの共有や再実行が面倒でした
# /// script ヘッダーで dependencies = ["ipython", "httpx", "loguru"] を宣言し、uv run jam_users.py を実行すると、uvが隔離環境と依存関係のインストールを処理します
#!/usr/bin/env -S uv run --script というshebangと実行権限を追加すれば、Unixシステムで uvだけがインストールされた状態 でも ./jam_users.py のように実行できます
スクリプト内で依存関係を宣言する
- 例の
jam_users.py は、ローカルAPI http://localhost:4000/v1/users を対象にテスト用ユーザーデータを準備します
- httpx でAPIリクエストを送信します
- IPython REPLに入り、レスポンスを確認しながらテストを続けます
loguru で削除と作成のログを残します
- 基本スクリプトは、ユーザー一覧を初期化した後、テストデータを再投入する流れです
GET /v1/users で既存ユーザー一覧を取得します
- 各ユーザーに対して
DELETE /v1/users/{id} を呼び出します
- 用意したユーザー一覧を
POST /v1/users にJSONで送信します
- その後
IPython.embed() でREPLを起動します
python jam_users.py で実行するには、httpx、IPython、loguru が実行環境にすでにインストールされている必要があります
- システムPythonにグローバルインストールしたり、仮想環境を別途作ったりする方法もありますが、どちらも実行前の準備が必要です
- グローバルインストールは、システムPythonをパッケージで散らかしてしまう可能性があります
- 仮想環境方式では、作成、アクティベート、インストール、実行の手順を自分で管理する必要があります
- どちらの方式でも、必要なパッケージと互換性のあるシステムPythonが必要です
uvで直接実行できるスクリプトを作る
- uv の
# /// script タグをスクリプトの先頭に入れると、ファイル内で依存関係を宣言できます
# /// script
# dependencies = ["ipython", "httpx", "loguru"]
# ///
- このヘッダーがあると、
uv run jam_users.py で実行できます
- uvがスクリプト用の隔離された仮想環境を作成します
- 必要な依存関係をダウンロードしてインストールします
- その仮想環境のコンテキストでスクリプトを実行します
- 通常のPython shebangである
#!/usr/bin/env python では、Pythonが # /// script コメントを無視するため、uvのスクリプトヘッダーを活用できません
- shebangにuv呼び出しを直接入れると、スクリプトを実行ファイルのように扱えます
#!/usr/bin/env -S uv run --script
# /// script
# dependencies = ["ipython", "httpx", "loguru"]
# ///
env の -S フラグは、後続の文字列を個別の引数に分割して env に渡すためのものです
chmod +x jam_users.py で実行権限を付与した後は、次のように直接実行できます
./jam_users.py
- この方法では、Unixシステムに uvがインストールされていれば、別途依存関係をインストールしたり仮想環境を管理したりせずにスクリプトを実行できます
- 複雑なPythonスクリプトを他のユーザーに渡すとき、実行前のシステム準備手順を長々と案内する負担を減らせます
1件のコメント
Hacker Newsの意見
UVそのものではなく全般的に、コメントでコード実行を制御するやり方が気になる
リンター指示や開発者メモにコメントを使うのはよいが、設定や実行関連データなら
UV_ENV = { "dependencies": { "requests": "2.32.3", "pandas": "2.2.3" } }のような形のほうがずっとよいと思うこの方式は有効なPython構文で、任意のコメントをパースする代わりに標準のデータ構造を使うため生成と検証がしやすく、何よりコメントをすべて削除してもコードは同じように実行されるべきだという原則を守れる
提案された方式も実行時には何もしないマジック定数なので、静的解析でしかパースされず、uvが読む代わりに別のツールが未使用コードと見なして削除してしまう可能性もある
いっそ
import uvの後にuv.exec(dependencies=["clown"], python=">=3.10")のように、uvに何をするかを直接呼び出させればよい初回実行は、この仮想的な
uvパッケージを見つけられる任意のPythonランタイムで行い、uvパッケージが仮想環境とPythonランタイムを用意した後、環境変数のようなフラグを付けてre-exec(3)すればよい2回目のランタイムでは、フラグを検知した
uv.execが何もしなければよいhttps://peps.python.org/pep-0723/
また、shebang行自体も実際にはコメントとして動作する
45年もの間あまりに深く根付いたため、それがシェルのコメントだという事実を人々があまり意識していないだけ
同じ依存関係をインストールしていれば、コードは同じように実行される
コード自体の意味を変えるというより、コードが実行される環境を変えているのであり、その意味ではシェルスクリプト先頭の
#!/bin/bashコメントと変わらないただしuvは依存関係を知るためにコードを実行したくない可能性が高いので、Python構文のごく限定されたサブセットである必要があるだろう
そもそもこういうものが必要だという事実は、言語の弱点を示している
import文自体が依存関係に関するすべての情報を伝えられるべきだここ数か月、HNではこの話題が非常に頻繁に出ており、最近の例は次のとおり
https://news.ycombinator.com/item?id=43500124
https://news.ycombinator.com/item?id=42463975
uvは好きだが、self-containedという表現には2つの点で同意しにくい
第一に、スクリプトを実行するにはuvがすでにインストールされている必要がある
シェルスクリプトでuvのインストール有無を確認し、なければcurlpipeでインストールするようにはできるが、ボイラープレートがかなり増えるし、curlpipe方式自体も好ましくない
第二に、ホームディレクトリのどこかに仮想環境を自動生成するのは、本当のself-containedではない
一度だけ実行してスクリプトを削除しても、その仮想環境は残って容量を占有し、uvのドキュメントでこうした一時的な仮想環境を自動的に整理するという保証は見つけられなかった
curl | shを使わなければならないという不満は妥当ただしパッケージマネージャーがuvをリポジトリに含め始めれば、次第に問題ではなくなっていくだろう
例えばuvはすでにAlpine LinuxとHomebrewで提供されている: https://repology.org/project/uv/versions
また、インラインスクリプトメタデータはPython標準である
システムにuvがなく、パッケージ化もされていないが、スクリプトに合うPythonバージョンがあるなら、pipxで実行できる: https://pipx.pypa.io/stable/examples/#pipx-run-examples
pipxははるかに広くパッケージ化されている: https://repology.org/project/pipx/versions
任意の開発者マシンではuvよりDockerのほうが一般的かもしれず、元記事も会社のプロジェクトだというので、それらしく思えた
ただし現状ではキャッシュをまったくしていないため、実行するたびにダウンロードが発生する点がぎこちなく、ボリュームで直せそうではある
こういう形: https://hugojosefson.github.io/docker-shebang/#python
それでも、実行時にインターネットから正体不明のものをダウンロードするならself-containedとは呼びにくく、本当に完全なself-containedはAppImageに近い
py2exeのようなものを使えば、self-containedな「Pythonスクリプト」を作れる
開発者には多くの問題が生じるが、ユーザーにとっての問題は最小化される
Nix でも同じ方式を使っており、shebang 行は次のような形になる
#! nix-shell -i python3 -p "python312.withPackages (pkgs: [ pkgs.boto3 pkgs.click ])"こうすればシステムに必要なのは Nix だけで、Python がインストールされている必要すらない
いちばん分かりやすい例は、すべての依存関係を指定した bash で、Nix shebang を使って高速な単一ファイルの Rust スクリプトを作ったこともある
https://nixos.wiki/wiki/Nix-shell_shebang
nix-shellの代わりに flake ベースのコマンドであるnix shellで同じことをする方法が気になる他のコメントにもあるように、「self-contained」という主張は
uvがインストールされていることに依存している本当に self-contained な Python スクリプトが欲しいなら、Nuitka コンパイラを見てみる価値がある
gRPC サービスの本番環境で問題なく使っていて、
nuitka --onefile run.pyを実行するだけで済むコンパイラなので、PyInstaller でまとめた元の Python プログラムより、生成されるバイナリが速いこともある
作者の GitHub ページには「可能な限り最高の Python Compiler を作るために、年老いて死ぬまで試み続けることが私の人生の使命だ」という文言がある
https://nuitka.net/
https://github.com/kayhayen
このパターンは本当に気に入っているが、残念ながら LSP とはうまく動かせなかった
Helix で pyright を使っていて、エディタを
uv run hx script.pyで起動しても駄目だったuv run --with whatever-it-is-i-need hx script.pyのようにすることはできるが、だんだん重複が増えていくuveスクリプトを使っている$ cat ~/.local/bin/uve#!/bin/bashtemp=$(mktemp)uv export --script $1 --no-hashes > $tempuv run --with-requirements $temp vim $1unlink $tempエディタが近いうちに
uv python find --scriptをサポートしてくれるといいかなり便利そうだ
Python ベースのプロジェクトを長期的に配布する場合、uv はより安全な選択肢なのか気になる
約5年前に依存関係管理に Anaconda を使っていたが、後にルールが変わり、従業員200人以上の組織顧客は Anaconda を無料で使えなくなり、商用ライセンス料を支払う必要が出たという rug pull を思い出している
開発を中止したり、今後の作業を別ライセンスのフォークに移したりすることはできるが、過去のライセンスを遡って変更することはできないので、現在存在しているものはオープンソースであることが保証されている
本当に心配なら、フォークを作って同期し続ければよい
実質的に他の OSS プロジェクトにもほぼ同じように当てはまる話なので、あまり心配しない
私の知る限り、conda はオープンソースだったことはなく、バイナリを配布してきた
https://github.com/astral-sh/uv?tab=readme-ov-file#license
conda-forge のパッケージは今でも無料で使える
uv は単に PyPI を使っているだけなので、問題が起きたら uv から pip や Poetry などに切り替えればよく、パッケージは引き続き同じ場所から取得することになる
そういう所有者は、結局思いつく限り最悪の金持ちに買収されるものだ
uv のコントリビューションガイドと issue をざっと見たが CLA は見当たらず、PyTorch ではコントリビューションガイドの一番上に CLA があった
それでも Anaconda の最後の FOSS 版にはコミュニティフォークがあるべきだった
Redis ではそうなったし、Redis も CLA を使っている: https://github.com/redis/redis/blob/unstable/CONTRIBUTING.md#redis-software-grant-and-contributor-license-agreement
絶対に CLA に署名してはいけない
むしろ copyleft プロジェクトにだけ貢献しようと思う
私たちは無償で働くにはあまりにも高い給料をもらっている
小さなユーティリティには、コンテナ化の代わりになる良い パッケージング代替案に見える
あとは同僚全員に uv をインストールするよう説得しなければならない
Ruby の bundler/inline に似ているように見える
Python にも似たものができてうれしいし、実際とても便利だ
https://bundler.io/guides/bundler_in_a_single_file_ruby_script.html
Windowsでこれを動かせた人がいるのか気になる
作業中のゲームMod用ツールでこのトリックを使いたかったが、shebangのトリックを動かせなかった
$> uv init --script .py$> uv add --script .py ...$> uv add --script .py --dev ...$> uv run .py役に立てばうれしい
出典: https://docs.astral.sh/uv/guides/scripts/
.pyファイルに関連付けるpy launcherはshebang行をサポートしている
数日前に投稿された同じテーマのブログ記事でも扱っていて、それによると
-Sは外す必要がある: https://thisdavej.com/share-python-scripts-like-a-pro-uv-and-pep-723-for-easy-deployment/https://news.ycombinator.com/item?id=43500124
自分では試しておらず、代わりにファイルの関連付けを変更して、すべての
.pyファイルがデフォルトでuv runで開くようにしたhttps://docs.python.org/3/using/windows.html#python-launcher-for-windows
https://peps.python.org/pep-0397/
https://pyinstaller.org
https://onor.io/2025/01/more-scripting-with-racket.html
このユースケースのおかげでuvが好きになったが、公式で非常に有用なPEPが公式のPythonツールでサポートされていないのは Zen of Python に反していると感じる
自分の見方では、Pythonが初めて「batteries included」ではなくなった瞬間だ
今ではシステムにPythonの依存関係マネージャーも2つ入ることになった
Pythonの依存関係管理について言いたいことが多いのは分かっているが、ここ数年はプロジェクトに
requirements.txtさえあれば、標準のpip+venvでしのいできたpyproject.tomlはtomllibライブラリより先に出ていたと記憶しているそのため数バージョンの間、Pythonが標準では読めない言語でモジュールを指定しなければならなかった
使われないメタデータを含める標準的な方法があるより、さらに悪い状況だ
そもそもだからこそメタデータなのであって、そうでなければ単にPython構文だったはずだ