nikkie-ftnextの日記

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

uvはinline script metadataを書ける! Guidesの「Running scripts」を読んだメモ

はじめに

左下から来るぞ! nikkieです。

このブログでたびたび取り上げているPythonのinline script metadata。
今回はuvのドキュメントを読みます

目次

inline script metadata

PEP 723で提案・採択された、Pythonスクリプトに書けるコメントです1

# /// script
# dependencies = ["httpx", "rich"]
# ///
import httpx
from rich.pretty import pprint

resp = httpx.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])

PEP 723をサポートしているuvやpipxといったツールでこのスクリプトを実行すると、ツールが仮想環境を用意して有効化し、依存ライブラリをインストールしてからスクリプトを実行してくれます。
自動化が好きでPythonスクリプトを頻繁に書いてきた私は、「仮想環境を人間が管理しなくていいのか!」と非常に感銘を受けました2

uvはpipxよりも積極的にPEP 723をサポートしているように見えたので、ドキュメントを読んでいきます。

% uv --version
uv 0.4.12 (Homebrew 2024-09-18)

uvはinline script metadataを書いてくれる!

https://docs.astral.sh/uv/guides/scripts/#declaring-script-dependencies

touch script.pyして、中を以下のようにします。
※pyproject.tomlがなく、script.pyだけのディレクトリで動かしています3

import httpx
from rich.pretty import pprint

resp = httpx.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])

inline script metadataを書いていないので、uv run4で実行してもエラー

% uv run script.py
Traceback (most recent call last):
  File "/.../script.py", line 1, in <module>
    import httpx
ModuleNotFoundError: No module named 'httpx'

なんと次のコマンドで、uvがスクリプトを編集して、inline script metadataを追加してくれます!!

% uv add --script script.py httpx rich
Updated `script.py`
+# /// script
+# requires-python = ">=3.12"
+# dependencies = [
+#     "httpx",
+#     "rich",
+# ]
+# ///
import httpx
from rich.pretty import pprint

resp = httpx.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])

dependenciesだけでなく、requires-pythonも追加されました。
uvが生成したmetadataを編集していくというのも、十分考えられますよね

Guides「Running scripts」のメモ

気になった点をザーッと書き出します。

  • Running a script with dependencies
    • inline script metadataを書いていなくても、uv run --withで実行できる
    • uv run --with httpx --with rich script.py
  • Improving reproducibility
    • PEP 723で見かけていないので、uv独自仕様と思われる
# /// script
# [tool.uv]
# exclude-newer = "2023-10-16T00:00:00Z"
# ///

uv supports an exclude-newer field in the tool.uv section of inline script metadata to limit uv to only considering distributions released before a specific date.

意訳「特定の日付より前にリリースされた配布物だけに制限して扱わせるための、tool.uvセクションのexclude-newerをuvはサポートする
思うに、実行時点で有効な依存の組み合わせでスクリプトを実行し続けるための仕組み、でしょうか

  • Using different Python versions
    • uv run --python
  • Using GUI scripts
    • On Windows uv will run your script ending with .pyw extension using pythonw:

終わりに

uvのGuideでinline script metadataに関係する箇所を読みました。
uvはめちゃめちゃ(一部やりすぎではと引くほどに)inline script metadataをサポートしています!

uv add --script <スクリプト.py> 依存ライブラリ ...inline script metadataが生えるのはとてもよさそうです!
exclude-newerはPEP 723外に勢いよく飛び出しすぎてる気もしますが...

仮想環境を都度作ってスクリプトを書いてきた私からは、inline script metadataはとてもありがたく、ツールによるサポートも今後増えていくと期待しています。


  1. ドキュメント https://packaging.python.org/en/latest/specifications/inline-script-metadata/
  2. 広くPython使いに知ってほしいとPyCon JP 2024で話します
  3. uvがPythonプロジェクトとは認識していない状況です。なお、仮にpyproject.tomlがあってもuv run --no-projectと指定すればよいと知りました
  4. https://docs.astral.sh/uv/reference/cli/#uv-run より uv run file.pyuv run python file.pyというコマンドの実行と同じです