はじめに
グルクン🐟、くくるちゃんを幸せそうな表情にしたので、しゅき... nikkieです
PyCon JP 2024でPEP 723(inline script metadata)の話をしてきました。
発表後に気づいた小ネタです。
目次
- はじめに
- 目次
- 別のPythonファイルからのimportをサポートしている!
- 種明かし:スクリプトのあるディレクトリがsys.pathに追加されている
- 思うに、1つのスクリプトを分割したくなったら、プロジェクトへの移行を考えたい
- 終わりに
別のPythonファイルからのimportをサポートしている!
あまりオススメしないですが、できることに気づきました1。
. ├── script.py └── mylib.py
script.py
(richとmylib.py
を使う)
# /// script # dependencies = ["httpx", "rich"] # /// from rich.pretty import pprint from mylib import fetch_peps data = fetch_peps() pprint([(k, v["title"]) for k, v in data.items()][:5])
mylib.py
(httpxを使う)
import httpx def fetch_peps(): resp = httpx.get("https://peps.python.org/api/peps.json") return resp.json()
この構成で、hatch, uv, pipx, pdmと試したところ2、すべて動作しました3。
% hatch run example/import-case/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') ]
種明かし:スクリプトのあるディレクトリがsys.path
に追加されている
importできるポイントは、script.pyと同じディレクトリにmylib.pyがあることです。
inline script metadataをサポートしたツールは、Python処理系を呼び出しています。
対話モードに入る方法を調べる中で知りました。
python example/import-case/script.py
のように、処理系にスクリプトのパスを指定して立ち上げたとき、
https://docs.python.org/ja/3/using/cmdline.html#cmdarg-script
スクリプト名が Python ファイルを直接指定していた場合、そのファイルを含むディレクトリが
sys.path
の先頭に追加され、そのファイルは__main__
モジュールとして実行されます。
つまり、script.pyがあるディレクトリがsys.path
の先頭に追加される4ので、mylibモジュール(mylib.py)が見つかり、importできるわけですね!
% PYTHONINSPECT=1 hatch run example/import-case/script.py >>> import sys >>> sys.path[0] '/.../pep723/example/import-case'
inline script metadataをサポートしたツールは渡したスクリプトごとに仮想環境を作りますから、metadataは渡すスクリプトに全ての依存を書いています。
- ツールは
script.py
に対して仮想環境を作る - その仮想環境が有効な状態で
import mylib
されるので、mylib.py
が使うライブラリもscript.py
のmetadataに書いて、仮想環境にインストールしておく
思うに、1つのスクリプトを分割したくなったら、プロジェクトへの移行を考えたい
最初に「あまりオススメしない」と書きましたが、inline script metadataをサポートしたツールの出番でなくなっているように思います。
例えば、さらにscript2.py
を追加したとすると
. ├── script.py ├── script2.py └── mylib.py
mylibの依存ライブラリをmetadataに繰り返し書かねばなりません。
# /// script # dependencies = ["httpx"] # /// from mylib import fetch_peps
これをやるよりは、2つのスクリプト用に共通の仮想環境を1つ作ったほうがよいと思います(少なくともこれはやりたい)
. ├── .venv/ # httpxやrichをインストール ├── script.py ├── script2.py └── mylib.py
また個人的には、Pythonプロジェクトにしたいタイミングですね。
PEP 723に書かれているユースケース5の1つに、単一スクリプトをプロジェクトにする際にmetadataが役立つとあります。
終わりに
inline script metadataをサポートしたツールで実行するスクリプトの中で、別のPythonファイルをimportできます。
Pythonスクリプトを使った開発の中でimportできるとき・できないときがあるな〜という経験をしてきましたが、処理系に渡したスクリプトのあるディレクトリがsys.path
に追加されることを知って謎が解けた感覚です。