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

UVとPEP 723の楽しみ

概要

uv は高速なPythonパッケージ・プロジェクト管理ツールで、Rust製。 PEP 723 により、スクリプト内に依存関係やPythonバージョンを記述可能。 uvxuv run を使えば、依存関係のセットアップ不要でワンオフスクリプト実行が容易。 YouTubeトランスクリプト抽出など、実用的な例も紹介。 従来よりPythonスクリプト実行のハードルが大幅に低減。

uvとPEP 723の活用

  • uv はRust製の高速Pythonパッケージ・プロジェクト管理ツール
    • Node/NPMの npx のような uvx コマンドを提供
    • 必要なPythonバージョンや依存関係を自動セットアップ
  • PEP 723 はPythonスクリプト内にメタデータ(依存関係やバージョン)を埋め込む仕様
    • スクリプト上部に requires-pythondependencies を記述
    • スクリプト単体で実行環境の再現が可能
  • uv runuvx を使うことで、事前準備不要で即座にスクリプト実行
    • 例:$ uvx ruff --versionで依存パッケージを即時インストール・実行
    • 例:$ uv run pep.pyでPEP 723メタデータ付きスクリプトをワンコマンド実行
  • スクリプト例:YouTube動画のトランスクリプト抽出
    • シェバン行:#!/usr/bin/env -S uv run --scriptでuvを明示
    • 必要なパッケージやPythonバージョンもスクリプト内メタデータで宣言
    • 実行方法:chmod +x ytt後、./ytt <YouTube URLまたは動画ID>で即実行

PEP 723メタデータ記載例

  • スクリプト冒頭に以下のように記述

    # /// script
    # requires-python = ">=3.11"
    # dependencies = [
    #   "requests<3",
    #   "rich",
    # ]
    # ///
    
    • 依存パッケージやバージョン条件を明示
    • 外部ツールやIDEがこの情報を利用可能

uv runによるワンオフスクリプト実行

  • uv run <スクリプト名>で依存関係を自動インストール後に実行
    • 例:uv run pep.py
      • 必要パッケージを24msでインストール
      • スクリプトの結果を即座に出力

YouTubeトランスクリプト抽出スクリプト例

  • シェバン行でuv run --scriptを指定
  • PEP 723メタデータでyoutube-transcript-api依存を宣言
  • コマンドライン引数でYouTube URLまたは動画IDを受け取り、トランスクリプトを取得・表示
  • 実行例:./ytt https://www.youtube.com/watch?v=zgSQr0d5EVg
    • 必要パッケージを10msでインストールし、トランスクリプトを即出力

Pythonワンオフスクリプトの新時代

  • これまでGoなどの自己完結型バイナリが一発実行に便利だった
  • uvPEP 723 の組み合わせで、Pythonスクリプトも同等の利便性を獲得
  • 例:YouTubeトランスクリプト抽出サーバーをPythonで即席実装
    • Githubリポジトリ: cottongeeks/ytt-mcp

参考リソース

まとめ

  • uvPEP 723 でPythonスクリプト実行が圧倒的に手軽
  • 依存関係やバージョン管理をスクリプト内で完結
  • ワンオフツールや小規模自動化に最適な選択肢

Hackerたちの意見

uvはちょっとしたサイドプロジェクトに使うには最高だね。uv runuv tool run、つまりuvxを組み合わせることで、GitHubからPythonスクリプトを超簡単に取得して、VM内にインストールして実行できるんだ。git cloneも、venvの作成やエントリー、pip installもいらない。そしてuvは速い — 本当に速い。pipよりも10倍速く動くから、何かおかしいのかと思っちゃうくらい。実際には、ちゃんと自分が望んでいたことをやってくれてるんだけどね。ドキュメントはちょっと荒削りだけど、それでも十分使う価値があると思ってるよ。

本当にそうだね。uvは、pyenvが自分の--help出力を表示するよりも早く依存関係を解決してインストールしてくれる。

本当に素晴らしいね。どんどん人気が出てきてるみたい。最初にsimonwのブログで見たんだ:https://simonwillison.net/2024/Dec/19/one-shot-python-tools/ それから、3月には別のブログ記事についての議論もあったよ:https://news.ycombinator.com/item?id=43500124 これがしばらくフロントページに残って、もっと広まるといいな。

ようやくPythonスクリプトが仮想環境を探し回らなくても動くようになった感じだね。今度は誰かがシェルスクリプトでも同じことをやってくれたらいいのに。シェルの世界では、パッケージング、依存関係管理、再現性がまだ石器時代に留まってる。今はまだcurl | bashでうまくいくことを祈るか、12の手動ステップと3つの欠けてる依存関係があるREADMEを頼りにするしかない。確かにNixはあるけど…時間や空間、Nixのマニュアルを超えてるなら別だけどね。Docker?素晴らしいけど、sedを動かすためにLinuxディストリビューションをダウンロードするのが合理的だと思えるならね。もっとシンプルで宣言的、人間向けに作られた中間的な解決策が必要だよ。

シェルスクリプトをPythonに移植することを考えてみたら?言語としては圧倒的に優れてるし、subprocess.check_callもそんなに悪くないよ。

Homebrewでできるかな?

新しいシェルスクリプトを書く必要があるの?依存関係をインストールできるなら、uvを使えばいいよ、あとはそれがやってくれるから。あと、https://babashka.org/にも惚れかけてるから、Clojureが好きならチェックしてみて!

シェルの世界では、パッケージ管理や依存関係の管理、再現性がまだ石器時代にいる感じだよね。個人的にはこのままでいいと思う。だって、そういうものが必要なスクリプトは、シェルを使うにはもう遅すぎるから。シェルスクリプトは小さくて、20行くらいが理想だよ。言語自体が使う価値がないくらいひどいから、大きなものには向かないよね。

シェルスクリプトの依存関係を解決する特定のケースでは、Nixは実際にすごく簡単だよ。スクリプトをパッケージするのはwriteShellApplicationコールで、実行するのはnix runだし。問題は、その特定のことをどうやってやるかが誰も文書化してないから、Nix全体を学ぼうとしないといけないことかな。だから、君が考えてることはこのNixのロジックのラッパーになるかもしれないね。

これにはresholveを使ってNixを使ってるけど、すごく気に入ってるよ。

この特定のユースケースに関しては、Nixはそんなに難しくないと思う。別のディストリビューションにNixをインストールするのは結構簡単だし、インストールしたらこんな感じで使えるよ。

#! /usr/bin/env nix-shell
#! nix-shell -i bash -p imagemagick cowsay
# 画像を50%にスケール
convert "$1" -scale 50% "$1.s50.jpg" && cowsay "done $1.q50.jpg"

確かにNixOSやNixのパッケージングは挑戦だけど、シェルスクリプトに使う分にはそんなに悪くないよ。

反応せずにはいられないんだけど、明らかに解決策はNixをDockerでシェルとして実行することだね。パッケージング、依存関係管理、再現性が理論的な最大値になるから。

昨日のスレッドとこのスレッドの間で、やっとuvを試してみることにしたんだ。速度とプロジェクトの依存関係管理の簡単さに感動してる。ドキュメントはもう少し改善が必要だと思うけど、特にrequirements.txtベースのワークフローからuvに切り替えるための明確な道筋が必要だね。それに、特定のプロジェクトのためのPythonバージョンをどう定義するかがちょっと混乱する感じがした(それは.python-versionとpyproject.tomlの両方で定義されてるから)。

これについては調べたことないけど、.python-versionファイルは、完全なTOMLパーサーを持ってない他のツールのために存在してると思ってた。

requirements.txtベースのワークフローからuvに切り替えるための明確なパスが必要だよね。uvx migrate-to-uvを試してみて。(https://pypi.org/project/migrate-to-uv/)

Python開発者向けのツールについての電子書籍を書いてるよ。公式ドキュメントの弱点をいくつかカバーしようとしたんだ。requirements.txtからの移行方法はこちら: https://pydevtools.com/handbook/how-to/migrate-requirements.... uvプロジェクトのPythonバージョンを変更する方法はこちら: https://pydevtools.com/handbook/how-to/how-to-change-the-pyt... 他に役立ちそうなトピックがあったら教えてね!

.python-versionとpyproject.tomlの両方に定義されている pyproject.tomlrequires-versionフィールドは互換性のあるバージョンの範囲を定義していて、.python-versionは開発に使いたい特定のバージョンを定義してるんだ。uv initで新しいプロジェクトを作ると、似たような感じになる(>=3.13と今日の3.13)。でも、時間が経つにつれてrequires-versionは通常.python-versionに遅れをとって、プロジェクトの最小サポートPythonバージョンを定義することになる。requires-versionはパッケージのメタデータにも含まれて、呼び出し側の依存関係解決に影響を与えることがある。例えば、公開したv1がPython 3.[古い]をサポートしていて、v2はサポートしていない場合とか。

特定のプロジェクトのためにPythonバージョンを定義する方法(.python-versionとpyproject.tomlの両方に定義されている) pyproject.tomlは他の開発者やエンドユーザーがあなたのコードを使えるようにするためのものなんだ。コードをPyPI用にパッケージ化して共有するとき、ビルドバックエンド(uvはそうじゃないけど、提供するために取り組んでいるみたい - https://github.com/astral-sh/uv/issues/3957 を参照)で配布可能なパッケージが作成されて、pyproject.tomlがユーザーに必要な環境(依存関係やPythonバージョン)を指定する。これはuv自体とは関係なく、相互運用可能なPythonエコシステムの標準なんだ。ここでバージョンの範囲が指定されているのは、他の人が複数のPythonバージョンであなたのコードを使えるようにするため。.python-versionファイルは、uvに特化して(つまり、他の誰にも)具体的に(つまり、正確なバージョン)開発環境を設定する際に何をするかを伝えるために使われる。(もちろん、すでに設定された環境を使うのも全然可能だよ。)

いいね、Rustも似たようなことをやってると思う。そこで他の言語での単一ファイルシェルタイプのスクリプトのアイデアを初めて知ったんだ(依存関係管理が含まれてるから、既存のスクリプト言語での単一ファイルスクリプトとは違うんだよね)[0]。もっと多くの言語がこのパターンを取り入れてくれるといいな。いろんなケースで非常に便利だから、gistsを回したり、シェルスクリプトで書かれるかもしれない小さなプログラムを書くのに役立つよね。[0] https://rust-lang.github.io/rfcs/3424-cargo-script.html

C#もね: https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-...

今のところ、uv run --scriptを使って埋め込まれたメタデータでちょっとした使い勝手の問題に直面しただけなんだ。スクリプトの変更をPython REPLでテストしたい時があるんだけど、ちょっとやりにくい。例えば、こんな感じで実行しなきゃいけないから: $ uv run --python=3.13 --with-requirements >> from script import X もっと使いやすい方法があればいいな。例えば、こんなの: $ uv run --with-script script.py 編集: こっちの方がいいね: $ "$(uv python find --script script.py)" >>> from script import X これでスクリプトに合ったPythonとvenvが立ち上がる。スクリプトを一度実行して作成する必要があるかも。

どうぞ、cat ~/.local/bin/uve #!/bin/bash temp=$(mktemp) uv export --script $1 --no-hashes > $temp uv run --with-requirements $temp vim $1 unlink $temp

こんなのを探してるんじゃないかな(重要なのは、最後の方にREPL呼び出しを埋め込むこと): https://gist.github.com/lostmygithubaccount/77d12d03894953bc... スクリプトから--interactiveみたいなCLIフラグを作れるよ。こういう小さなTyper CLIをよく作るんだけど、別の開発スクリプトでは--sqlでDuckDBのSQL REPLに入れるようにしてる。

何が起こってるの?このスレッド全体が有料のAmazonレビューみたいに見えるんだけど。

起こってるのは「14の基準があるから15番目を作る必要がある」ってのが、実際に機能したってことだね。

時々、レビューが現実と一致することもあるよね。

数年前、Pythonスクリプトが自分の依存関係(記事に出てくるuvxみたいな)を自動でインストールするツールを作るのが面白いと思ったんだけど、Python自体以外の外部ツールは必要ないっていうのが条件だったんだ。デメリットは、スクリプトの最初に貼り付けなきゃいけないちょっと変な行がたくさんあることかな :D 興味がある人はpypiにあるよ(pysolate)。

あと、これもあまり流行らなかったやつね: https://github.com/fal-ai/isolate ちょっと違うけど面白いよ!

著者と同じように、仕事とプライベートでクロスプラットフォームのPythonの一発ものや個人スクリプトを使うことが多くなって、Goを手放してる。Pythonの型チェックがもうちょっとマシだったらいいのに。tyやpyreflyなんかが状況を少しでも改善してくれるのを楽しみにしてる。

[遅延]