uvでPythonスクリプトが自己完結!?環境構築の手間を省く裏技
引用元:https://news.ycombinator.com/item?id=43519669
UVに限らず、コメント欄でコードの実行をコントロールするのってマジ勘弁って感じ😅。リンターとか開発者向けのメモは全然ありだけど、設定とか実行に関わるデータはもっと良い方法があると思うんだよね。
例えばこんな感じ。
UV_ENV = {
”dependencies”: { ”requests”: ”2.32.3”, ”pandas”: ”2.2.3” }
}
これなら、Pythonの文法として正しいし、標準的なデータ構造を使ってるから解析も楽ちん。それに、コメントを全部消してもコードがちゃんと動くっていう原則にもちゃんと従ってるのが良いよね👍
めっちゃ同意だけど、もう一歩踏み込んで考えるなら、それって結局ランタイムでは何もしてないマジック定数じゃん?UVが解析する為だけにあるみたいな。他のツールが未使用コードとして消しちゃう可能性もあるし。それって、マジックコメントと大差ない気がするんだよね🤔。
だったら、UVに直接指示を出せば良くない?
import uv
uv.exec(
dependencies=[“clown”],
python=“>=3.10”,
)
from clown import nose
最初の呼び出しは、uvパッケージを見つけられるPythonでOK。uvパッケージがvenvを設定して、環境変数とかでフラグ立てて再実行する感じ。
2回目の実行では、uv.execはフラグを検知して何もしない、みたいな。
それにはちゃんと理由があるみたいだよ。
>https://peps.python.org/pep-0723/#why-not-use-possibly-restr…
リンクありがとね😊。PEPの主張に対する反論としては、(1)Pythonを実行しようとしてるんだから、Pythonパーサーは手元にあるはずだし、(2)当面は過去のバージョンのPythonも解析できるはず、ってことかな。まぁ、ちょっと強引な気もするけどね。依存関係と実際のコードで言語を分けるのは、長期的な安定性には役立つかもね。
それって結局、PythonがUVを実行することになるってことでしょ?開発の観点から色々な影響がありそうじゃん?理論的にはそれが一番クリーンな方法だと思うけど、shebangもコメントみたいなもんだし、ここでドグマティックになる価値があるのかは微妙だよね🤔
その議論には賛成できないな🙅。JSON形式に限定すれば、アドホックな構文よりもずっとメンテナンスしやすいし、拡張性も高いと思うよ。最初は管理しやすくても、徐々に解析が複雑になっていくのは避けたいじゃん。
UVの正当性のひとつは、Pythonに依存しないからブートストラップ問題がないってことだよね。現状、UVを使うには「UVのサイトからインストールコマンドを取得して実行(まだインストールしてない場合)。UVでスクリプトを実行。」の2ステップで完了する。これ以上シンプルにはできないでしょ。UVをインポート可能にするってことは、Pythonがすべてのシステムに簡単にインストールできる前提になるから、それならUVは必要なくなるよね。
それだと、本来UVに依存しなかったコードがUVに依存することになるよね。スクリプトのコメントに依存関係を示す仕様はPEP723で、ツールに依存しないんだよ。
>”In your case that’s uv doing the parsing but another tool might delete it as unused code.”
それが狙いだよ。特定のツールのためだけにあるんだから。使われなかったら、実行中のアプリに影響を与えないようにしたい。コメントと同じだね。
だって、UVをインストールしてないとコードが動かなくなっちゃうじゃん。
Pythonのバージョンによって文法が違うから、新しい構文だと古いPythonじゃ最初の数行すら実行できないかもね。例えばPython3.6でprint(”hello”) match 123: case _: pass
ってやると、”hello”すら表示されないよ。
>”自己完結型”スクリプトの肝は、どこでも動くこと。 uv は専用の cpython runtime を使うけど、uv の起動前にスクリプトが実行されると、それが台無し。
記事のテクニックなら、古い Ubuntu LTS 環境でも最新 Python のスクリプトが動くけど、GP のやり方じゃダメ。
代わりに、uv に何をすべきか指示する呼び出しを作ったらどう?
大事なのは、これが uv 特有の機能じゃないってこと。 将来的には他のパッケージマネージャーも実装する Python の標準機能になる予定。 だから、どんな解決策も uv だけでなく、標準に準拠したパッケージマネージャー全てで動作する必要がある。
これは uv の発明じゃなくて、uv は他のツールと同じように標準の PEP 723 を使ってるだけだよ。 https://peps.python.org/pep-0723/
前に言ったように、uv への批判じゃなくて、この全体的なアプローチへの批判だよ。
それもそうだね。 命令的なコードを解析・評価するより、最小限の権限の原則に従って宣言的なデータにする方が、柔軟性があると思う。
コメントを使うのは、そもそも shebang 行と同じ。 45年も経つから、みんなシェルコメントだって気づかないだけ。
>コードからコメントを全部消しても、依存関係をちゃんとインストールすれば、挙動は変わらないはず
その通り。 コード自体の意味は変わらない。コードが動く環境が変わるだけ。 それはシェルの先頭にある#!/bin/bash
コメントと変わらない。
完全に同意。 こういうのが標準化されると良いな。
問題は、uv は依存関係を見つけるために何も実行したくないから、かなり制限された Python のサブセットになる必要があるってこと。
そもそも、これが求められてるってことは、言語の弱点を示してる。 import 文自体が依存関係の情報を全部伝えるべき。
UV は PEP 723[1] を実装しただけ。 これは PyPA Inline Script Metadata[2] になった。 もはや仮のものではなく、標準化されたんだ! Python に、コメントじゃない方法でこの機能を提供する方法がなかったのは残念。 [1] https://peps.python.org/pep-0723/ [2] https://packaging.python.org/en/latest/specifications/inline…
そもそもこれが必要なこと自体が、言語の弱点を浮き彫りにしてるよね。import
文自体に依存関係に関するすべての情報を含めるべきじゃない?どの言語がスクリプトのimport
文で依存関係のバージョンを伝えてるの?
だいたい賛成だけど、それって これ、ここ数ヶ月の そうだよね、 そうじゃなかったら、10種類のディストリビューションすべてがプログラムをパッケージ化して、それが更新されるのを気長に待つしかないじゃん。 いやいや、自分でパッケージングして署名キー付きで配布する方が良くない?.debとか.rpm作れば、エンドユーザーの大部分カバーできるじゃん。 それって現状より悪くない?開発者の多くはArch LinuxとかNixOSとか、一般の人にはマイナーなディストリ使ってるし。作者が持ってるドメインのHTTPSリンクから もしシェルスクリプトにキー検証が組み込まれてたら、出所検証の観点からは大差ないかもね。でもそれってレアじゃん?それに、OSのパッケージマネージャー使えば、アンインストールがめっちゃ簡単。 俺は10個もOS/ディストリ使ってないけど、俺のツールの潜在的なユーザー全体で見れば、10個以上なんて余裕でありえる。 docker+uvで同じような問題を解決しようとしたけど、なんとなくできたよ。ランダムな開発環境だとuvよりdockerの方が一般的だからね(特にtfaがgong projectだって言ってるし)。 普通、プログラムを実行する前に何かインストールする必要があるよね。だからuvをインストールするのも、そんなに悪いことじゃないと思うな。でも、プログラムを実行する時にインターネットから色々ダウンロードするから、これって自己完結型とは言えないよね! 100%同意。py2exeみたいなものを使うと、自己完結型の“pythonスクリプト”が作れる。開発者にとっては問題が多いけど、ユーザーにとっては問題が少ない。 細かいことだけど、uvのパッケージの重複排除機能のおかげで、仮想環境は固有の依存関係がない限り容量を消費しない。 >uvのドキュメントには、一時的な仮想環境が自動的にクリーンアップされるという記述が見当たらない。 詳しく見てみたんだけど、uvは必要なパッケージをキャッシュディレクトリ(~/.cache/uv)にインストールしてるみたい(まだそこになければ)。だから、uv clearとかでキャッシュを消さない限りパッケージは残るんじゃないかな。venvディレクトリを新しく作るわけじゃなくて、uvはパッケージを中央の場所にリンクさせて、既にあれば再利用するっぽいね。 たぶん、uvはvenvを作るときにスクリプト名、Pythonのバージョン、依存関係のハッシュを作るんだと思う。だから、どれも変わらなければvenvを再利用するんじゃないかな。 Nixでも同じことやってるよ。shebang行はこんな感じ: それはそうだけど、PyPIパッケージにはまだnixpkgsでパッケージ化されてないものもたくさんあるから、uvほど万能なアプローチではないよね。 > you don’t even need Python to be installed! うん、同じテクニックはどの言語にも使えるよ。一番わかりやすいのはbashで依存関係を全部指定するやつだけど、nix shebangを使ってRustのシングルファイルスクリプトをハックしたこともある。 Nihをインストールする方が、uvをインストールするよりもずっと大変だよ。 Nixはセットアップと理解にいろいろ必要。uvはPATHにあるバイナリだけ。 それ、ちょっぴり面倒なんだよねー。詳しくはこのリンク見てみて! もしかして 他のコメントにもあるように、“自己完結型”って言うには このパターン、マジで好きなんだけど、LSP (pyright, in Helix) で動かすのがマジ無理ゲー。uv経由でエディタ起動してもダメだった ( 俺の自作uveスクリプト、マジ汚いけど晒すわ。 なるほど、こんな感じ? そーそー。cleanup処理が複雑なら関数にしてもOK。 これマジ使える!uvってPythonプロジェクトのデプロイで長期的にも安全な選択肢?昔anacondaで痛い目見たから心配なんだよねー。5年くらい前に依存関係管理に使ってたんだけど、ルール変わって従業員200人以上の企業は有料になっちゃったんだよね。 uvはMITかApache-2.0ライセンスだよ[0]。開発が止まったり、ライセンスが変わったりする可能性はあるけど、過去のバージョンはオープンソースが保証されてる。心配ならフォークして同期すればOK。でも、他のOSSプロジェクトも同じだし、気にしすぎないで良いと思うよ。condaは元々オープンソースじゃなかったし、バイナリ配布だったはず。 オープンソースだからってメンテされるとは限らないよね。uvはRustで書かれてるってのがちょっと特殊。Pythonに興味あるRustの開発者がアクティブにいるかにかかってるってことじゃん? マジですごいのは、インラインメタデータの形式がPEPで認められてるってこと。だから、uvがダメになっても、他のツールでサポートできる可能性があるってことだね。 anacondaの件はリポジトリの引き上げでしょ(conda-forgeのパッケージは今でも無料で使える)。 俺の理解だと、プロジェクトがContributor Licensing Agreement(CLA)を結んでる場合、著作権をプロジェクトのオーナーに譲渡することになるから、ライセンスの変更が可能になるんだよね。(最終的には、考えうる限り最悪の金持ちに買収される運命)。 CLAが無いとライセンス変更はできないけど、オープンソースライセンスってそもそも取り消せないじゃん。 それって法廷でほとんど試されてないんだよね。最近の判決では、少なくともOSSには対価性があるから、正当な理由なしに一方的に終了することはできないってことになってるけど、USCにはその正当化事由が規定されてる(ただし、2~10年の事前通知とか、特定の期間内に行う必要があるとか、厄介な要件がある)、取り消し不能って書いてあるライセンスでも。 これって、小さいユーティリティをコンテナ化する代わりに使うには良いパッケージングの選択肢になりそうだね。同僚全員にuvをインストールさせるように説得しなきゃ。 Uvはマジで速いよ。それが後押しになるはず。 俺たちにとっての問題は、SCAの脆弱性スキャナがまだuvに対応してないってことなんだよね。 uvの依存関係をrequirements.txtとしてエクスポートする中間SCAステージを追加すればいいんじゃない? これってRubyのbundler/inlineみたいなもんかな?[1] Pythonで同じようなのが出てきて嬉しいなー。めっちゃ便利そう! 誰かこれWindowsで動かせた人いる?ゲームMOD用のツールで使いたかったんだけど、shebangのトリックがうまくいかなくてさ。 WindowsとLinuxでよく使ってるよー。手順はこんな感じ: 残念ながら、 –debフラグがスクリプトで使えないのは興味深いね。記憶だけで書いちゃったけど、スクリプトに開発依存関係をインストールした気がしてた。ただの勘違いだったみたい。訂正ありがとう。 ちょっと脱線するけど、シェルの例では山括弧は避けるべきだよ。間違って入力しちゃうと全部パーになっちゃうからね。 Windowsの通常のCPythonインストーラーはpy launcherをインストールして、.pyファイルに関連付けるんだよね。py launcherはshebang行をサポートしてる。 へー、面白いね。俺が使ってるワークフローはCPythonインストーラーをスキップしてuvだけ使ってるよ。 Windowsはshebang行をサポートしてないと思うけど、uvを.pyファイルに関連付ければ同じ結果になるよ。こんな感じかな: でも拡張ハンドラーの登録はサポートしてるみたいだよ[1]。だから、スクリプトに例えば.pyuvって名前を付けて、”.pyuv”ファイルのハンドラーとして“uv run –script %1”とか、uvを実行するのに必要なものを登録すれば、うまくいくんじゃないかな。uvが変なことしてなければ。 PyInstallerみたいなのを使うのもアリだよ。 役に立つかわかんないけど、誰かがRacketで同じようなことをPoweScriptでやる方法を投稿してたよ。 WSLからならShebangはちゃんと動くはず。 このユースケースのおかげでuvを好きになったんだけど、公式の(しかも、めっちゃ役立つ!) PEPが公式のPythonツールでサポートされてないのは、PythonのZenに反してると思うんだよね。もっとコメントを表示(1)
import
文の前に、有効な静的構造であることを指定できるってこと。future statements
とか他のimport
の間に入れるってこと?shebang
を文字通り使ってるよね。HN
でめっちゃ話題になってるよね。最近の例だと、https://news.ycombinator.com/item?id=43500124
https://news.ycombinator.com/item?id=42463975
uv
は好きだけど、”自己完結型”って主張には異論があるんだ。
1) スクリプトはuv
が既にインストールされてることを前提としてる。最悪、uv
がインストールされてるかチェックして、されてなかったらcurlpipe
でインストールするシェルスクリプトにすることもできるけど…ボイラープレートが増えるし、curlpipe
パターン自体が微妙じゃん?
2) ホームディレクトリにvenv
を自動作成するのも、自己完結型とは言えない。使い捨てスクリプトを実行して削除しても、venv
は残って容量を食う。一時的な仮想環境が自動的にクリーンアップされるって記述もuv
のドキュメントに見当たらないし。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
curl | sh
はマジで嫌なやり方だから絶対に使っちゃダメ。Dockerfile
では便利で適切な場合も多いよ。curl | sh
するより、署名キー付きの.deb/.rpmの方がなんで良いの?.deb/.rpmだって任意のシェルコマンド含んでるじゃん。
これ動くけど、何もキャッシュしないから毎回ダウンロードするのが面倒なんだよね。ボリューム使えば直せるかも?
>https://hugojosefson.github.io/docker-shebang/#python
俺にとって、完全な自己完結型ってAppImageみたいなもの。
それは良い指摘だね。少なくとも、スクリプトを何回か実行したら再利用されるのかな。
#! nix-shell -i python3 -p ”python312.withPackages (pkgs: [ pkgs.boto3 pkgs.click ])”
これで、必要なのはシステムにNixが入ってることだけ。PythonすらインストールされてなくてもOK!もっとコメントを表示(2)
TFAの場合もまさにそうで、uvがPythonのインストールをその場で行ってくれる。
https://nixos.wiki/wiki/Nix-shell_shebangnix shell
(flakeベースのコマンド)でnix-shell
の代わりに同じことをするにはどうすればいいの?
https://nix.dev/manual/nix/2.22/command-ref/new-cli/nix3-she…#! /usr/bin/env -S nix shell
って手もあるかも?uv
がインストールされてる前提じゃん?
マジで自己完結させたいなら、Nuitkaコンパイラがおすすめだよ[0]。プロダクション環境のgRPCサービスで使ってるけど問題なし! ”nuitka –onefile run.py”でOK。マジ卍。コンパイラだから、Pyinstallerより速いし。
作者のGitHub[1]には「最高のPythonコンパイラを作るのが俺の使命!」って書いてあるよ。
[0] https://nuitka.net/
[1] https://github.com/kayhayenuv run hx script.py
)。uv run --with 必要なやつ hx script.py
とかできるけど、めんどくさすぎ。
bash
$ cat ~/.local/bin/uve
#!/bin/bash
temp=$(mktemp)
uv export –script $1 –no-hashes > $temp
uv run –with-requirements $temp vim $1
unlink $temp
エディタがuv python find --script
に対応してくれたら最高。trap .. EXIT
使ってunlinkみたいな処理を遅延させるの、マジ便利だよ。スクリプトが途中で止まってもちゃんと動くし。temp=$(mktemp)
trap 'unlink $temp' EXIT
# Do things
[0] https://github.com/astral-sh/uv?tab=readme-ov-file#license
PyPI使ってるから、パッケージ管理とか開発ツールとしては使ってもいいかな。最悪pipとかに戻ればいいし。でも、パッケージのランタイム依存としては使わないかな。それにはpyinstaller使う。
こういうのって、開発者がリポジトリで使うユーティリティスクリプト向けだと思う。個人のユーティリティをuvに縛り付けたくないし。依存関係が必要ならパッケージ作る方が楽だしね。
uvはpypi使ってるだけだから、uvからpipとかpoetryに変えるだけ。パッケージは全部同じところから来るし。
uvのコントリビューションガイドとかissuesを覗いてみたけど、CLAは見当たらなかった。PyTorchでは、コントリビューションガイドの冒頭にCLAの記載があったよ。
あと、Anacondaの最後のFOSSバージョンのコミュニティフォークがあっても良かったはず。Redisではそうだったし、RedisはCLA使ってるし。
https://github.com/redis/redis/blob/unstable/CONTRIBUTING.md…
みんな、絶対にCLAにサインしちゃダメだよ。どうせなら、コピーレフトプロジェクトに貢献しようぜ。ただ働きするには給料高すぎるって。もっとコメントを表示(3)
[1] https://bundler.io/guides/bundler_in_a_single_file_ruby_scri…
>uv init –script <script_name>.py
>uv add –script <script_name>.py
>uv add –script <script_name>.py –dev <dev_pkg1> <dev_pkg2> …
>uv run <script_name>.py
参考になるといいな!
Source: https://docs.astral.sh/uv/guides/scripts/uv add --dev
は--script
と一緒には使えないみたい:
>uv -V
uv 0.6.10
>uv add –script foo.py –dev ruff
エラーが出るんだよね。
Pythonのインラインスクリプトのメタデータは、開発依存関係を標準化してないみたい。pyproject.tomlで開発環境を整える方法について最近コメントしたから見てみて! https://news.ycombinator.com/item?id=43503171
数日前に同じトピックのブログ記事が投稿されてたよ。それによると-Sは省略しないといけないみたい。試してないけど、.pyファイルをuv runで開くようにファイル関連付けを変更したよ。参考:https://thisdavej.com/share-python-scripts-like-a-pro-uv-and… https://news.ycombinator.com/item?id=43500124 https://docs.python.org/3/using/windows.html#python-launcher… https://peps.python.org/pep-0397/
ftype Python.File=C:¥Path¥to¥uv.exe run %L %*
CPythonインストーラーを使ってない場合はPython.Fileファイルタイプが定義されてないかもしれないから、assocで設定する必要があるかも:
assoc .py=Python.File
インストール段階でこれをやるとか。
[1]:https://learn.microsoft.com/en-us/windows/win32/shell/fa-fil…
https://pyinstaller.org
https://onor.io/2025/01/more-scripting-with-racket.html
Pythonが“batteries included”じゃなかったのは初めてだよ。
今では、システムに二つのPython依存性マネージャーがあるし。Pythonの依存性管理については語るべきことが山ほどあるのは知ってるけど、requirements.txtがある限り、pip+venvで何年もやってこれたんだ。