nikkie-ftnextの日記

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

uvお試し記:uv venvで作った仮想環境でpip installしてはいけません。uv pip installしましょう

はじめに

銃を抜いたからには 命を懸けろよ、nikkieです。

uvを試したところ、「思ってたのと、違う!」となった(=私が誤解していた)点のメモ書きです。
学び:uv付けたならuv付け通せよuvカットはできません)

目次

前提:Python Monthly Topicsより、uv使ってみよう

venv + pip と uv の比較記事です1
uvの速度について言及されています。

  • python -m venv vs. uv venv
  • pip install vs. uv pip install

uvで仮想環境を作ったら、続くコマンドにもuvを付け続ける

macOSbrewで入れました2

% uv --version
uv 0.1.31

📌こちらの使い方をしていきましょう(コマンドにはuvを付け続ける)

% uv venv
% uv pip install kojo-fan-art

(仮想環境を有効にしていないですが、)これでuvが作った仮想環境にインストールできます3

% source .venv/bin/activate
(work) % kojo-day

(work) % python -q
>>> import the_solitary_castle_in_the_mirror

uvが作る仮想環境は、venvが作る仮想環境と同じと考えてはいけない

uvが作る仮想環境とvenvが作る仮想環境を同じと誤解したために小さくハマりました。

uv venvは仮想環境の有効化を促します4
(私の環境ではpyenvが管理するPythonが選択されています)

Using Python 3.10.9 interpreter at: /.../.pyenv/versions/3.10.9/bin/python3
Creating virtualenv at: .venv
Activate with: source .venv/bin/activate

私がやってしまったのは、有効化してからのpip install(俺みたいになるな!)

% rm -rf .venv
% uv venv
% source .venv/bin/activate

(work) % pip install kojo-fan-art
(work) % python -q
>>> import the_solitary_castle_in_the_mirror
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'the_solitary_castle_in_the_mirror'

(uvの仮想環境を有効にして)pip installしたのにimportできませんでした。

実は、uvの仮想環境にはpipがない5んですね6

(work) % python -m pip list
/.../.venv/bin/python: No module named pip

なので、pip install kojo-fan-artしたpipは、仮想環境の外のpipなのです!

(work) % which pip
/.../.pyenv/shims/pip

現在有効にしているuv製仮想環境にインストールできていなかったので、importできなかったわけです。
学んだのは、uvコマンドで仮想環境をはじめたら、依存ライブラリのインストールではuvを付け続けるということです

終わりに

uvをvenv(やvirtualenv)同様の仮想環境と考えたために小さく躓いた点を記録しました。
uvを使うときは、venvなどのことは一度忘れuv venvuv pip installを使っていくことになるのですね。

uvのソースコードをちょっと覗いた感じ、Pythonの仮想環境やpipの動きをRustで再実装している印象を受けました(uv venv7uv pip install8)。
Python実装のツールを使わずにRustに置き換えているから速いという理解です。

一方、再実装の中では完全な互換性をもたせない方針9のようで、今回経験したようにPythonの仮想環境の知識をそのまま適用できない点もあるようです(ゼロリセット!)。
uv pipは、pipそのものやそのラッパーではないのだと思います。
pipと近い振る舞いをするように実装されているだけで、uvの仮想環境にpipはありません。


  1. 読んで気になった点のアウトプット
  2. brew install uv https://github.com/astral-sh/uv/tree/0.1.31?tab=readme-ov-file#getting-started
  3. Virtual environments by default https://github.com/astral-sh/uv/blob/0.1.31/PIP_COMPATIBILITY.md#virtual-environments-by-default
  4. 奇しくも私も.venvという名前に行き着きました
  5. venvでも、できらあ!
  6. uv venv --seedで「Install seed packages (pip, setuptools, and wheel) into the virtual environment」とのことです。でもどんなユースケースで使うんだろ?
  7. ここから https://github.com/astral-sh/uv/blob/0.1.31/crates/uv/src/commands/venv.rs
  8. ここから https://github.com/astral-sh/uv/blob/0.1.31/crates/uv/src/commands/pip_install.rs
  9. 途中でも引いた https://github.com/astral-sh/uv/blob/0.1.31/PIP_COMPATIBILITY.md 参照。私の気持ちも置いときます

F#に慣れるために、自分だけのクソコンソールアプリケーションを作る

はじめに

からあげ帝国、バンザイ!! nikkieです。

春ですね。F#する春です🌸

慣れるための素振りをしていきましょう。

目次

F#で自分だけのクソコンソールアプリケーションを作る

この記事では、F#で小さなコンソールアプリケーションを作ります。
我が主(=皇帝)のからあげさんの記事に、オマージュ捧げていく形となります。

Python1

.
├── unko/
│   ├── __init__.py
│   └── unko.py
└── pyproject.toml

F#版(今回作ったもの)

.
├── Unko/
│   └── Unko.fs
├── Program.fs
└── UnkoFSharp.fsproj
% dotnet run
puripuri

順を追って見ていきましょう。

コンソールアプリケーションを作る

dotnet new console -lang F#コマンドでスキャフォールディングします。

このドキュメントとは異なり、Project.fsには

printfn "Hello from F#"

としか書かれていません。
これをドキュメントにあるようにしていきます。

let deru = printfn "puripuri"

[<EntryPoint>]
let main argv =
    deru
    0

deru関数を定義し、main関数の中で呼び出しています。

ちなみにコンソールアプリケーションはこちら

「明示的なエントリ ポイント」を使ってますね

モジュールを導入

このあたりはGPT-4に聞きつつ、ドキュメントにあたって進めました。

1つのファイルにderu関数があるのはクソの系譜の美学に反するので、別のファイルに分離していきます。
これは「モジュール」というそう2

ルート直下のUnko.fsに切り出してもよいのですが、ディレクトリを導入することにしました(Unko/Unko.fs)。

module Unko

let deru = printfn "puripuri"

fsprojファイルに追加します。

  <ItemGroup>
+    <Compile Include="Unko/Unko.fs" />
    <Compile Include="Program.fs" />
  </ItemGroup>

ここは順番に意味があるそう。
逆順だとビルドできません。

Program.fsではopenします。

+open Unko
-let deru = printfn "puripuri"

[<EntryPoint>]
let main argv =
    deru
    0

頻繁に使用されるモジュールと名前空間に open キーワードを使用すると、そのモジュールまたは名前空間のメンバーを参照するときに、完全修飾名の代わりに短い形式の名前を使用することができます。

Unkoモジュールのderu関数は、Unkoモジュールをopenするとderuとして参照できるようです

こうしてF#版Unkoが完成しました!🙌
Unko/Unko.fsの文字列を変更してdotnet runすると、出力される文字列が変わります。

終わりに

F#に慣れるために、小さなコンソールアプリケーションを作る素振りをしました。
モジュールを作り、それをopenするんですね(open Unko

fsprojファイルはファイルごとにCompileを書く必要があるようです。
ディレクトリを指定できたら楽な気がするんですが、F#の大きなライブラリはどうしてるんでしょうか3

P.S. nikkieによるunkoオマージュギャラリー

皇帝のお言葉


  1. pyproject.tomlを使ったバージョンで示します(P.S.参照)
  2. 名前空間というやつでもできるかも(宿題事項)
  3. VS Code拡張のIonideに案内された fantomas のリポジトリを見ると、なにかわかるかも

pip installのオプションに--no-cacheなんてあったっけ? --no-cache-dirを短縮できるのか!!

はじめに

3月のPython Monthly Topicsを読んでいたnikkie氏。

パッケージのインストールpip installuv pip installの処理時間を比較している。あれ、--no-cache?」

$ time pip install pandas --no-cache

$ time uv pip install pandas --no-cache

pip installに、私の知らないオプションがある...!」(※--no-cacheのこと)

目次

pip installのオプションに--no-cacheなんてあったっけ?

ドキュメント

pip installのドキュメントを見てみましょう。
https://pip.pypa.io/en/stable/cli/pip_install/#options

--no-で始まるオプションはいくつかありますが、--no-cacheはヒットしません。

コマンドライン--helpを渡します。
pip install --helpでもpip --helpでも「General Options」として--no-cache-dirが出てきます

  --no-cache-dir              Disable the cache.

pipのドキュメントではここにあります1
https://pip.pypa.io/en/stable/cli/pip/#general-options

ヘルプメッセージやドキュメントには--no-cacheというオプションは記載がないのですが、Python Monthly Topicsのpip install --no-cacheというコマンドは手元で動作しています
なぜなんでしょう?

実装

pipのリポジトリを検索して見つけたのがこちら。
https://github.com/pypa/pip/blob/24.0/src/pip/_internal/cli/cmdoptions.py#L717-L724

no_cache: Callable[..., Option] = partial(
    Option,
    "--no-cache-dir",
    dest="cache_dir",
    action="callback",
    callback=_handle_no_cache_dir,
    help="Disable the cache.",
)

この箇所は、functools.partialを使って、optparse.Optionの一部の引数を部分適用しています。

--no-cacheというオプションはなさそうでした。

なぜ--no-cacheと指定できるかを考えていて思い出したのは、argparseの例。
https://docs.python.org/ja/3/library/argparse.html#prefix-matching

parse_args() メソッドは、デフォルトで、長いオプションに曖昧さがない (先頭文字列が一意である) かぎり、先頭文字列に短縮して指定できます:

-baconオプションを-bacと指定する例があります。

pipの実装に使われているoptparseは

バージョン 3.2 で非推奨: optparse モジュールは廃止予定であり、これ以上の開発は行われません。argparse モジュールを使用してください。

という存在ですが、optparseでも引数の短縮形はサポートされている記載がありました!
https://docs.python.org/ja/3/library/optparse.html#how-callbacks-are-called
opt_strのところ。

例えば、ユーザが --foobar の短縮形として --foo をコマンドラインに入力した時には、opt_str は "--foobar" となります。

どうやらpipの--no-cache-dirオプションを短縮して、--no-cacheと書いていた(書けていた)のではないかと思われます。

--no-cache-dirを短縮できることを検証

optparseで検証プログラムを書いてみます2

>>> options  # --no-cache-dirと指定
<Values at 0x102402550: {'no_cache_dir': True}>
>>> options2  # --no-cacheと短縮して指定
<Values at 0x102402790: {'no_cache_dir': True}>

短縮して指定できることが確認できました。

終わりに

pip install --no-cacheは私の知らないオプションではありませんでした。
--no-cache-dirの短縮で、optparseやargparseでサポートされています(標準ライブラリ以外に関しては未調査です)。

短縮できる便利さはありつつも、個人的には--no-cache-dirと短縮せずに使っていきたいですね。
--no-cacheとも--no-cache-dirとも指定できるのはなぜ?」と混乱の元になりますし、--no-cacheの情報はググラビリティが低めでした。
The Zen of Pythonにも「Explicit is better than implicit.」とありますし。

P.S. uv pip installでは?

% uv --version
uv 0.1.31

uv --helpと打つと

  -n, --no-cache
          Avoid reading from or writing to the cache

おそらくここ
https://github.com/astral-sh/uv/blob/0.1.31/crates/uv-cache/src/cli.rs#L10-L28

alias = "no-cache-dir"とあるので、--no-cache--no-cache-dir(の2つ)をサポートしていそうです。

% uv pip install pandas --no-cache-dir

pipの実装は短縮ができるので、--no-cache-dもいけます(実際にやるかはおいておいて)。
ところがuvは上記の二者択一です。--no-cache-dはサポートされません。

% uv pip install pandas --no-cache-d
error: unexpected argument '--no-cache-d' found

  tip: a similar argument exists: '--no-cache-dir'

めちゃめちゃ重箱の隅ですけど、uvのオプションの指定の実装はpipと完全互換とは言えないのですね。


  1. 直リンク https://pip.pypa.io/en/stable/cli/pip/#cmdoption-no-cache-dir
  2. parse_argsにはデフォルトでsys.argv[1:]が渡ります https://docs.python.org/ja/3/library/optparse.html#optparse.OptionParser.parse_argsコマンドラインで何回も実行して検証するのは少し手間なので、プログラム中で引数を渡すことにしました

素振りログ:kfpのDSLで書いたパイプラインを、Kubeflow PipelinesでもVertex AI Pipelinesでも実行する

はじめに

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

先日、小さな気づきを記事にしました。

以下の宿題に取り組みます。

過去記事に沿ったYAMLファイルを当初Vertex AI Pipelinesにアップロードしたのですが、これは弾かれてしまいます。

小さな(練習用)パイプラインをkfp 2系でKubeflow PipelinesでもVertex AI Pipelinesでも動かします

目次

kfpで書いたパイプライン

こちらの記事をもとにしました。

わかったこと

  • componentにするPythonの関数には型ヒントが必要
    • ただジェネリック型のlist[]表記は現時点で非対応。listlist[Any]の意なので書きたくなく、typing.Listを使った
  • 参考記事で使っていたkfp.components.func_to_container_opは2.7.0にはなかった
    • 参考記事はcomponentの定義と、pipelineの定義を分けている(componentのYAMLとpipelineのYAMLができるっぽい)
    • 上で示したやり方は、パイプラインとして動かすDockerイメージにkfpがインストールされる(必要なのかもしれないが、関数の中身が依存しているものではないから除けないかと考えている)

ローカルのKubeflow Pipelinesにて

過去にKubeflow Pipelines 2.0.3・kfp v1で実行していました。

今回の差分

  • Kubeflow Pipelines 2.0.3 -> 2.1.0
  • Kubernetes環境は、Docker Desktop -> Rancher DesktopKubernetes v1.28.6)
  • DSLコンパイルするkfpも2系に上げています(1.8.22 -> 2.7.01

コマンドを流してKubeflow Pipelinesのリソースができるのを待ちます
https://www.kubeflow.org/docs/components/pipelines/v2/installation/quickstart/
「Deploy a KFP standalone instance into your cluster」参照2

export PIPELINE_VERSION=2.1.0

kubectl apply -k "github.com/kubeflow/pipelines/manifests/kustomize/cluster-scoped-resources?ref=$PIPELINE_VERSION"
kubectl wait --for condition=established --timeout=60s crd/applications.app.k8s.io
kubectl apply -k "github.com/kubeflow/pipelines/manifests/kustomize/env/dev?ref=$PIPELINE_VERSION"

試行錯誤メモ

  • Rancher DesktopのPreferences、Virtual MachineのCPUとMemoryは少なめなので(少し)増やす必要があった3
  • controller-managerがOOMKilledされていた
    • kubectl editしてmemoryのlimitsを30Miから引き上げた(動いているのをtopしたところ70Mi使っているので、次回は100Miくらいにすればよさそう)
  • proxy-agentが起動しないのは、諦めた
    • ログを見ると、curl http://metadata.google.internal/computeMetadata/v1/instance/zoneしていて「Could not resolve host: metadata.google.internal」で落ちてしまう

port-forwardしてWeb UIは動きました。
手元でcompileしたYAMLファイルをuploadします。

GCPのVertex AI Pipelinesにて

こちらにも手元でcompileしたYAMLファイルをuploadします。

終わりに

pipeline_rootや永続化という宿題は残っていますが、kfp(v2)が提供するDSL機械学習パイプラインを定義し、それをKubeflow PipelinesでもVertex AI Pipelinesでも実行できることを体験しました。

Vertex AI Pipelines、各社テックブログでもたびたび見かけるのですが、Kubeflow Pipelinesの経験から全然知らない技術ではないと言えたので、記事を読んでいけそうと思えました。


  1. 先日の気づきの記事で使ったのと同じバージョンです
  2. 最後のコマンドだけがv1のときと違いました(「/env/dev」ではなく「/env/platform-agnostic-pns」)。もしかすると、立ち上がらなかったリソースと関係あるかもしれません
  3. 私と同じM1 Macで環境構築した記事でもオススメされてました。「I also recommend you to adjust the # CPUs and the Memory (GB) in Preferences > Virtual Machine to avoid Out of Memory errors (OOM).

macOSにF#の環境を構築する

はじめに

シャーッ! nikkieです。

春ですね(* ̄ω ̄*)🌸1
ということで、環境構築していきましょう!

目次

F#

F# は、簡潔性、堅牢性、パフォーマンスの高いコードを書くためのユニバーサル プログラミング言語です。

OCamlから多くの要素を引き継いだ関数型とオブジェクト指向のマルチパラダイムである。
型安全であり、型推論の機能をもつ。

F#の開発環境

macOSで環境構築

.NET SDKインストーラをダウンロードし、それを実行するだけと、めちゃ簡単です。

% dotnet --version
8.0.204

dotnet fsi対話モードとなります

> printfn "Hello World!!";;
Hello World!!
val it: unit = ()

VS Codeにも拡張を入れました。

終わりに

F#、初めての概念が多く言語としては難しいと感じますが、環境構築は詰まることなく行きました!
F#のコーディングは痛気持ちいい体験ですね3

なぜ突然のF#?
こちらをどうぞ

まだまだ上手くなれそうでとってもワクワクです


  1. NEWS|TVアニメ「サクラクエスト」公式
  2. 別記事より
  3. ゴミ捨て場の決戦を観に行ったら、谷地ちゃんも言ってました

Weights & Biases(WandB)ってどんなツール? データを記録だけしてみる第一歩

はじめに

わんだほー!
きゃー!〜〜!!💚 nikkieです

Weights & Biases(ウェイツ・アンド・バイアスィズ)、触ってみました。

目次

技術書典15『WandBで始める実験管理』

LLMに関するMeetupのアーカイブが多くあって知っていたWeights & Biases1
https://www.youtube.com/@WeightsBiasesJapan

アーカイブではLLM(や機械学習モデル)の訓練で便利と聞きましたが、どんなツールかは触って理解しているわけではありませんでした。

また、こちらの技術同人誌を積ん読していました。

リンク集リポジトリがこちらです。

いまこそ紐解くとき!

ランダムな数値をWandBに記録してみる

第2章「WandB 入門」の最初のコードを素振りします(2.1)。
機械学習モデルは訓練せずに、ランダムな数値をWandBに記録だけします。
WandBとは何かを掴むのにうってつけの例だと思いました

  • Python 3.11.8
  • wandb 0.16.6
    • アカウントも開設しています
    • https://www.wandb.jp/ からサインアップ。この記事の範囲は無料です2

環境変数WANDB_API_KEYを設定しました。
API KeyはUser settingsの中にあります(Danger Zone)

WandBにbasic-introプロジェクトを作って、3回のRunを記録するコードを書いています。
1回あたりの記録のコードは以下です。

with wandb.init(
    project="basic-intro",
    name=f"experiment_{run}",  # 例: experiment_0
    config={
        "learning_rate": 0.02,  # この例ではダミーの値
        "architecture": "CNN",
        "dataset": "CIFAR-100",
        "epochs": 10,
    },
) as run:

    epochs = 10
    offset = random.random() / 5
    for epoch in range(2, epochs):  # 8回ランダムな数値を生成
        loss = 2**-epoch + random.random() / epoch + offset
        acc = 1 - loss
        run.log({"acc": acc, "loss": loss})

スクリプトを実行すると、Runが3回記録されます。
ローカルのwandbディレクトリに記録してから、WandBのWebアプリ側にも記録されるようです。

ブラウザで見た画面です(Runsを見ています)

グラフも見られます

感想(終わりにに代えて)

ちょっとしか触っていないので誤解しているかもしれませんが、「MLflow3と似ているな」という印象です。

  • 両ツールとも、モデルに関するデータ(例:訓練中のロスの値)をローカルに保存
  • WandBはローカルだけでなくWebアプリにも保存。ダッシュボードとして見られる
    • 他方MLflowも、ローカルでダッシュボードを立てられます。またDatabricks社が提供するサービス版があるように記憶しています

モデルに関するデータを記録する機能について細部を見れば違いがあるかと思いますが、両ツールとも機械学習まわり(特に実験管理)のペインを解消するツールなのだと思います。
今回取り上げた技術同人誌では、WandBのいろいろな事例が掲載されていたので、もうちょっと触ってみたいところです。


  1. 「LLMの評価方法」を視聴して書いたブログ
  2. パーソナルアカウントの範囲は無料です。チームで共有する機能は有料です https://www.wandb.jp/pricing
  3. 先日触りました

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は役割が違うと思うのです