nikkie-ftnextの日記

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

pipx runとPYTHONINSPECT環境変数の補足:仮想環境を作るときは環境変数を指定できない

以前書いたpipx runのtipsを補足します。

% PYTHONINSPECT=1 pipx run ./script.py
>>> 

目次

Pythonスクリプトをpipx runで実行した後に対話モードに入るには、PYTHONINSPECT環境変数を指定する

pipx runpython -i相当の動きをさせるために、環境変数PYTHONINSPECTを指定する方法を見出しました。
https://docs.python.org/ja/3/using/cmdline.html#envvar-PYTHONINSPECT
pipx runのプロセスが変わってpythonとなるのですが、環境変数PYTHONINSPECTも渡ることで、対話モードに入ります。

味をしめて使っていく中で、PYTHONINSPECT=1 pipx runがハングする事象に気づいたのです。

pipx runは仮想環境を作る

pipx runの動きを観察すると以下のようでした1

  1. 仮想環境を作る
  2. 仮想環境に依存ライブラリをインストール
  3. 仮想環境でスクリプトを実行

毎回1-3を行うわけではなく、1の仮想環境は一定期間キャッシュするようです。

Download the latest version of a package to a temporary virtual environment, then run an app from it.
The environment will be cached and re-used for up to 14 days. (pipx run --helpより)

すでにある仮想環境を使わないオプションが--no-cache

Do not reuse cached virtual environment if it exists

実装はsubprocess.runで仮想環境を作る

上記の1の箇所の実装なのですが、subprocess.runpythonを呼び出しています

pipx runにてvenv.create_venv(venv_args, pip_args)
https://github.com/pypa/pipx/blob/1.6.0/src/pipx/commands/run.py#L101

create_venv()メソッドの実装 https://github.com/pypa/pipx/blob/1.6.0/src/pipx/venv.py#L158

# 関数の中から抜粋しています
cmd = [self.python, "-m", "venv"]
venv_process = run_subprocess(cmd + venv_args + [str(self.root)], run_dir=str(self.root))

cmdは言ってしまえばpython -m venv .venvのようなものです。
ここにPYTHONINSPECT=1が副作用をもたらします。
仮想環境を作ったプロセスが対話モードに入ってしまう(と思われる)ので、pipx runは仮想環境作成中の表示でハングします。

再現実験

% pipx --version
1.6.0

pipx runのように、別プロセスでpython -m venvを呼び出して仮想環境を作るプログラムを用意しました。

import subprocess

result = subprocess.run(["python", "-m", "venv", ".venv"])
print(f"{result.returncode=}")

(出来上がる仮想環境は都度消しています)

通常の実行

% python reproduce.py
result.returncode=0

python -iスクリプトが流れた後に対話モードに入ります(ハングしません)

% python -i reproduce.py
result.returncode=0
>>>

ハングするケースです

% PYTHONINSPECT=1 python reproduce.py

別のターミナルでpsすると、python -m venv .venvのプロセスがあります。

nikkie          13556   0.0  0.0 410967856  15456 s000  S+    1:47AM   0:00.03 python -m venv .venv

これをkillすると、スクリプト実行中のターミナルは対話モードに入ります。
killにより、スクリプトを実行しているpythonプロセスに返ってきて、-iが効いた)

% PYTHONINSPECT=1 python reproduce.py
result.returncode=-15
>>>

まとめ:pipx run環境変数PYTHONINSPECT

pipxが管理する仮想環境の状態に思いを馳せないといけないのがあまり好みではないのですが2、以下の流れになると思います。

  1. inline script metadataだけ書いてpipx runしてしまう
    • ここで仮想環境の作成が完了する想定
  2. 以降は、PYTHONINSPECT=1 pipx runで対話モードを駆使しながらサクサク開発
    • キャッシュされた仮想環境を使っており、新規に作ることはないのでワークする

ただし、pipx run --no-cacheするときは(新たな仮想環境を作るので)PYTHONINSPECTは指定しない

以上、pipx runを使っていてPYTHONINSPECT環境変数指定のためにハングしてしまうことがある事象とその対処案の共有でした。


  1. ソースコードを読んでいってより正確にする余地が残っています
  2. プルリクエストを出すタイミングなのかもしれません