世界を動かす技術を、日本語で。

Uv: 依存関係を持つスクリプトの実行

概要

  • uv はPythonスクリプトの依存関係管理と実行を自動化するツール
  • 仮想環境の手動管理なしで スクリプトごとに依存関係を分離
  • インラインメタデータ やコマンドライン引数で依存関係を宣言可能
  • 依存関係のロック や再現性向上のための機能も搭載
  • Shebang やGUIスクリプト、異なるPythonバージョンにも対応

uvによるPythonスクリプトの実行と依存関係管理

  • Pythonスクリプト は、通常 python <script>.py の形式で実行

  • uv を利用することで、依存関係の管理や仮想環境の構築を自動化

  • Python環境 ごとにパッケージを分離することで、スクリプト間の干渉を防止

  • uv run コマンドで依存関係のないスクリプトを即時実行可能

    • 例:
      • example.py
        print("Hello world")
        
      • $ uv run example.py
        Hello world
        
  • 標準ライブラリのみ利用 の場合も追加作業不要

  • 引数の受け渡し標準入力からのスクリプト実行 もサポート

  • プロジェクトディレクトリpyproject.tomlが存在)では、デフォルトでプロジェクトの依存関係もインストール

    • プロジェクト依存を無視するには --no-project オプションを利用

依存関係付きスクリプトの実行

  • 追加パッケージ が必要な場合、--withオプションで依存関係を指定
    • 例:
      • $ uv run --with rich example.py
  • バージョン指定複数依存関係 も対応
  • プロジェクト依存と組み合わせ も可能だが、--no-projectで除外可能

インラインメタデータによるスクリプト管理

  • Python標準のインラインメタデータ で依存関係やPythonバージョンをスクリプト冒頭に宣言

  • uv init --scriptで雛形を作成

  • uv add --scriptで依存関係を追加・更新

    • TOML形式でスクリプト冒頭に記載される

      # /// script
      # dependencies = [
      #   "requests<3",
      #   "rich",
      # ]
      # ///
      
  • インラインメタデータ利用時 はプロジェクト依存は無視される(--no-project不要)

  • requires-python で必要なPythonバージョンも宣言可能

  • 依存関係なし の場合も空リスト必須

シェバングによる実行ファイル化

  • シェバング#!/usr/bin/env -S uv run --script)でスクリプトを直接実行可能
  • 実行権限を付与後、./greetのようにコマンドとして利用
  • インラインメタデータ と組み合わせて依存宣言も可能

代替パッケージインデックス

  • --index オプションでPyPI以外のインデックス利用可能
  • インラインメタデータにインデックス情報が追加される
  • 認証が必要な場合 は公式ドキュメント参照

依存関係のロックと再現性向上

  • uv lock --script で依存関係を.lockファイルに固定
  • ロックファイルが存在する場合、以降の操作はロック内容を再利用
  • exclude-newer フィールドでパッケージ取得日を制限し、将来の実行時の再現性を担保
    • RFC 3339形式で日時指定

Pythonバージョンの指定

  • --python オプションで任意のPythonバージョンを指定して実行
  • 未インストールの場合は自動ダウンロード
  • 詳細は公式ドキュメント参照

GUIスクリプトの対応

  • .pyw拡張子 のファイルはWindowsでpythonwを使用して実行
  • 依存関係付きGUIスクリプト--withオプションで対応

次のステップ

  • さらなる詳細は コマンドリファレンスuvのツール実行・インストール方法 のガイドを参照

Hackerたちの意見

これが僕の絶対のお気に入りのuv機能で、uvに切り替えた理由でもあるんだ。git-hooksに依存関係のあるスクリプトがいくつかあって、メインのvenvには入れたくないんだよね。#!/usr/bin/env -S uv run --script --python 3.13 この機能のおかげで、独自のvenvを作らずに依存関係を使えるようになったし、開発者には「brew install uv」って指示を出すだけで済むんだ。

正直、これはすごい(すごすぎる)機能だと思う。

完全に同意だね。UVのおかげで、たくさんのPythonをGolangに移行する予定だった大きなプロジェクトがストールしちゃった。まだたくさん移行されるけど、小さなスクリプトみたいなものはもう範囲外になったね。

ちょっとお邪魔しますが、-Sフラグが必要な理由って誰か知ってる?BSD環境で試してみたけど、/usr/bin/env -S uv run --python 3.11 python/usr/bin/env uv run --python 3.11 pythonは同じことをする(Pythonのインタラクティブシェルを起動する)んだ。envのマニュアルページはあまり明確じゃないし、最終的な結果が同じに見えるんだよね(「与えられた文字列を複数の文字列に分割し、各結果の文字列をenvユーティリティへの別々の引数として処理する...」)。

「スクリプトの依存関係を宣言する」っていうのがめちゃくちゃ便利だよね。https://docs.astral.sh/uv/guides/scripts/#declaring-script-d...

# /// script
# dependencies = [
# "requests

これをscript.pyとして保存すれば、「uv run script.py」で指定した依存関係を使って実行できるんだ。依存関係は一時的な仮想環境に自動でインストールされるから、全く考えなくていいのが魔法みたい。これはPythonのPEP 723の実装なんだよね。https://peps.python.org/pep-0723/ Claude 4はこのトリックを知ってるから、「インラインスクリプト依存関係付きのPythonスクリプトを書いて」って頼むと、ちゃんとやってくれる。例えば、https://claude.ai/share/1217b467-d273-40d0-9699-f6a38113f045 そこにあったプロンプトは、「httpxとclickを使って大きなファイルをダウンロードし、進捗バーを表示するインラインスクリプト依存関係付きのPythonスクリプトを書いて」だったんだ。Claude 4以前は、これをするための特別な指示を含むカスタムClaudeプロジェクトを持ってたけど、もう必要ないね。https://simonwillison.net/2024/Dec/19/one-shot-python-tools/

一瞬、requestsの横にハートの絵文字があるのかと思った。

PEP723はpipxやhatchでもサポートされてるけど、最近はuvが(当然ながら)注目を集めてるね。他にもpip-toolsがロードマップにサポートを持ってるよ。https://github.com/jazzband/pip-tools/issues/2027

シェバンモードもすごく便利で、実行ができるんだよね。例えば、./script.sh #!/usr/bin/env -S uv run --script # /// script # dependencies = [ # "requests

これクールだね! 同じようなことをGoでもやってみようかなって思ったけど、ちょっと怪しいアイデアだね: https://github.com/imjasonh/gos (別に推奨してるわけじゃないけど、どうなるか気になって試してみたら、まあまあうまくいったよ!)

これクールだけど、正直言うと魔法のコメントじゃなくて、組み込みの言語構文にしてほしいな。魔法のコメントってちょっとダサいし。いつかは…(組み込みの構文にするにはアーキテクチャ的な問題があるのは分かってるけど、魔法のコメントは外部ツールが解析しやすいし、Pythonのコアはパッケージや依存関係についての知識が限られてるから…でも、いつかは…)

おお、いいね!スクリプト内依存関係を持つuv専用のシェバンを使って幸せなユーザーだったけど、uv lock --script example.pyコマンドで特定のスクリプト用のロックファイルを作成できるのは、次のレベルに行った感じ!これがこんなに自然に感じるのに、Pythonのパッケージングが20年以上も経ってからようやく出てきたのはすごいよね。

単一のスクリプトで依存関係をロックする使い道は何なの? 俺の組織にとって便利なのは、ロックファイルに宣言された依存関係を例えばtrivy fs uv.lockでスキャンできること。これで既知のCVEが含まれているコードを実行していないか確認できるんだ。

これは単一ファイルのスクリプトにしか対応してないから注意してね。もしモジュールを持つプロジェクトがあって、モジュールが依存関係を宣言したい場合、これは使えないよ。uvは呼び出されたファイルで宣言された依存関係しか取得できないから。マルチファイルのプロジェクトの場合は、pyproject.tomlを用意しないといけないよ。詳しくは https://docs.astral.sh/uv/guides/projects/#managing-dependen... を見てね。どちらの場合でも、スクリプトやプロジェクトの作成者はuv addを使えるけど、単一ファイルの場合は--scriptを追加しなきゃいけないよ。

ここで、僕が作ったシンプルなJupyterカーネルを紹介するね: https://github.com/tobinjones/uvkernel これは「uv」と「iPython」を使ったミニマルなラッパーで、記事の機能をJupyterノートブック向けに提供してるんだ。他のプロジェクトに似てるけど、僕の実装は一番干渉が少なくて、Jupyterエコシステムの良い「市民」だと思うよ。進行中のプロジェクトもあるよ: https://github.com/tobinjones/pep723widget これはUI付きでノートブックの埋め込まれたスクリプトの依存関係を管理するJupyterプラグインを提供してるんだ。注意 — これは部分的にバイブコードされてて、まだ初期段階だからね。

あと、marimoもあるよ。俺の使い方では、Jupyterからの素晴らしいアップグレードになってる。TFAスタイルで使ってる(ノートブックはただのPythonファイル)。ノートブックのUIでは、スクリプトのヘッダーにパッケージを追加できるんだ。独立した再現可能な環境で、たくさんのノートブックを一つのディレクトリに持てるのがすごくいい。marimoノートブックは、gitにコミットする時のdiffも簡単だし、ノートブック内にAIコーディングアシスタントもいるし、反応的なUIフレームワーク、Pyodideを使ってブラウザで実行できる機能もある。さらに、古いJupyterノートブックを翻訳してくれるよ。俺にとって、uvがパッケージマネージャーに対するもので、marimoはノートブックに対するものだね。https://docs.astral.sh/uv/guides/integration/marimo/

一つの言語にどれだけのパッケージマネージャーが必要なんだろう?シンプルな言語だけど、設定が本当にひどいんだよね。これがその一つなのか、それとも次を待つべきなのかな?

試してみることをおすすめするよ。すごく話題になってるけど、理由があると思う。これがその一つだね。

俺的には、普段必要なのはpip-toolsとvenvだけなんだけど、uvはその二つを自然にまとめてくれるし、めっちゃ速いんだよね。

私も同じ気持ちだったけど、uvが競争相手を排除しちゃったみたい。OSのパッケージマネージャーを使ってuvをインストールするだけで、(隔離された)Pythonインタプリタもダウンロードしてインストールできるからね。

uvをデフォルトにして、このゲームを終わらせてほしいな。

これはかなり素晴らしい。学生にPythonコードを渡すと、いつも「どうやって実行するの?」って質問されるんだけど、答えるのが大変だったんだ。今は、uvをインストールして(コマンド一つで)実行するように言えるから楽になったよ。

この機能をMCPサーバーで使ってるんだけど、神のような存在だよ!どうやってやってるかの詳細は、 https://blog.toolkami.com/toolkami-shttp-server/ で見られるよ。

uvとスクリプトをデプロイすることで、使いやすくなる小さなものがたくさんあるよ。Golangの多くのユースケースはuvで置き換えられると思う。

このテクニックでハマったことがあるんだけど、家のインターネットが切れたときにルーターを再起動するスクリプトで使ったら、インターネットがないとuvがスクリプトに指定された依存関係をダウンロードできなくて、スクリプトが失敗しちゃった。幸い、書いた後に気づいたから、実際に動かす前に必要な依存関係を事前にインストールするようにセットアップを見直したよ。でも、俺がやりかけたミスをしないで! エアギャップで動かす必要があるコードには使わない方がいいよ! uvのキャッシュがあっても、キャッシュミスが起こることもあるからね。

でも、次回以降の実行のためには、一度だけ実行すればいいんじゃないの?

UVのこの機能大好き! "インストール"せずにjupyter notebookを立ち上げるためのワンライナーはこちら:uv run --with jupyter jupyter notebook すべてが一時的な仮想環境に入れられて、後でクリーンアップされるんだ。プロジェクトから実行すれば、その依存関係も拾ってくれるのが最高だね。