nikkie-ftnextの日記

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

Pythonスクリプトの未来。pipxが部分的にサポートしたInline script metadata(PEP 723)を触る

はじめに

ナイスゲーム!!👏👏👏 nikkieです。

今週のPyCoder's Weeklyから興味深かった記事に沿って手を動かしました。
未来は、意外と近くにある🤗

目次

「Inline run dependencies in pipx 1.4.2」

PyPIアプリストアのように使っちゃおう構想のpipx。
PyPIにあるCLIアプリケーションをpipxで環境を分けて管理できちゃいます(pipx install

(↑ macOSにpipxをインストールする手順も上の記事にあります)

記事「Inline run dependencies in pipx 1.4.2」ではpipx runの新機能が取り上げられます1
題材はこちらのスクリプト

# /// script
# dependencies = ["rich"]
# ///

import rich

rich.print("[blue]This worked!")

richはサードパーティライブラリです。

サードパーティライブラリを使うオススメ手順は、仮想環境2を切ってpip install rich
richをインストールせずにこのスクリプトは実行できません。

% python -V
Python 3.10.9
% python ./print_blue.py
Traceback (most recent call last):
  File "/..././print_blue.py", line 5, in <module>
    import rich
ModuleNotFoundError: No module named 'rich'

richというライブラリが見つからずに落ちています。

で す が、pipx runスクリプトを渡すと、実行できちゃうんです!!3

% pipx run ./print_blue.py
This worked!

青字で出力されています。
未来ずら〜〜

秘密を明かすと、このスクリプトの冒頭には# /// scriptで始まるコメントで表したメタデータがあります。
pipxはそれを解釈して、richをインストールしてくれます。
richがある環境でスクリプトを流すので、実行できるのです。

ちなみに./print_blue.pyという渡し方ですが、pipxがPyPIにパッケージを探しに行かないようにするため、「カレントディレクトリのスクリプト」と指定するそうです。

Remember to include the ./ to ensure pipx run doesn’t try to find a PyPI package named print-blue-py

PEP 723 – Inline script metadata (Status: Accepted)

Abstractより

This PEP specifies a metadata format that can be embedded in single-file Python scripts (略)

(意訳)このPEPは、単一ファイルのPythonスクリプトに埋め込むメタデータの形式を具体的に示したもの

How to Teach Thisがキャッチアップしやすいです。

メタデータのフィールドは2つ

  • dependencies
  • requires-python

スクリプトの実行に必要な依存ライブラリと、実行するPython処理系のバージョンをメタデータとして記載できるわけですね。

Exampleスクリプトをpipxで動かしましょう(内容としてはrichの例と同じです)

# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "requests<3",
#   "rich",
# ]
# ///

import requests
from rich.pretty import pprint

resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])
% pipx run ./script.py
[
│   ('1', 'PEP Purpose and Guidelines'),
│   ('2', 'Procedure for Adding New Modules'),
│   ('3', 'Guidelines for Handling Bug Reports'),
│   ('4', 'Deprecation of Standard Modules'),
│   ('5', 'Guidelines for Language Evolution'),
│   ('6', 'Bug Fix Releases'),
│   ('7', 'Style Guide for C Code'),
│   ('8', 'Style Guide for Python Code'),
│   ('9', 'Sample Plaintext PEP Template'),
│   ('10', 'Voting Guidelines')
]

pipxは(あくまで今は)dependenciesのみ限定サポート

macOSbrewで入れたpipxを使っています。

% pipx --version
1.5.0

今回取り上げた記事にもあるのですが、執筆時点のpipxのPEP 723 サポートはdependenciesのみと限定的です。

There’s also a requries-python field, which is currently ignored by pipx, (略) (原文ママ)

requires-pythonが無視されるので、Exampleはpython3.104でもpipx runできちゃいます。

% pipx run ./script.py --python=python3.10
[
│   ('1', 'PEP Purpose and Guidelines'),
(略)

プルリクチャンスですね

終わりに

PEP 723のInline script metadataのdependenciesをサポートしたpipx(1.4.2以降)を触ってみました。

これはかなり便利な気がします。
パッケージまでいかないPythonスクリプトにこのメタデータ(コメント行)を書けば、他の人の環境でも高い再現度で動かせるわけです!
Pythonってちょっとしたスクリプトが書きやすいな〜」と思っているのですが、このPEPはそこをさらに強化するという印象です。

PEPのTooling buy-inには、Hatchが挙がっています5
興味を惹かれますね。

P.S. pipxの「Found a space in the home path」

この記事を書くにあたり1.5.0に上げて使ったところ、pipx側になにか変更が入ったのか、以下のメッセージが出ていました。

⚠️ Found a space in the home path. We heavily discourage this, due to multiple
    incompatibilities. Please check our docs for more information on this, as
    well as some pointers on how to migrate to a different home path.

discussionに沿ってre-installしたら解消しました(「How to fix」参照)


  1. https://github.com/pypa/pipx/releases/tag/1.4.2 該当のプルリクエスト(の1つ)は
  2. 参考
  3. HomebrewのpipxはHomebrewのpython@3.12に依存しているようです(上で示したpython -Vはpyenvによるものです)
  4. pyenvを使っていて、python3.10, 3.11, 3,12とバージョンを取り揃えております
  5. MypyとRuffは、なんで挙がっているんでしょう? Pantsbuild(やPex)はHatchと似たツールだと思うので納得なのですが、MypyとRuffは役割が違うと思うのです