Macへuvをインストールして試行錯誤したメモ
はじめに
Pythonの勉強で使ってるUbuntuとは別に、手元のMacでもPythonの実行環境が欲しくなったので、今回は以下のようにみなさん一押しのuvを入れてみました。
ちなみに最近のMacはデフォルトでPythonが入っておらず、使おうとすると「コマンドラインデベロッパツール」のインストールを勧めてくる親切設計です。
でも、今回はuvでPythonを入れたい(Pythonのバージョン管理もuvでできると聞いている)のでここはキャンセルして、先にuvを入れてみます。
uvのインストール
公式の手順で挑みます。
Installation methods(インストール方法)
スタンドアロン インストーラーのmacOSの手順で進めます。
$ curl -LsSf https://astral.sh/uv/install.sh | sh
downloading uv 0.7.2 aarch64-apple-darwin
no checksums to verify
installing to /Users/ega/.local/bin
uv
uvx
everything's installed!
To add $HOME/.local/bin to your PATH, either restart your shell or run:
source $HOME/.local/bin/env (sh, bash, zsh)
source $HOME/.local/bin/env.fish (fish)
指示に従ってPATHを通すためにコマンドを実行します。使っているシェルはecho $SHELL
で確認できますが、私はZshでした。
$ echo $SHELL
/bin/zsh
ちなみにsource
は今動いているシェルプロセス内で動かす指定ですね。環境変数の反映は同一プロセスで指定しないとすぐに反映されません。Windowsではコマンドプロンプトでバッチファイルを動かすと同一プロセスになりますがLinuxはデフォルトが逆なので注意です。
$ source $HOME/.local/bin/env
実行されるスクリプトの中身はこんな感じでした。PATHに~/.local/bin
がなければ先頭に追加する形みたいですね。
$ cat ~/.local/bin/env
#!/bin/sh
# add binaries to PATH if they aren't added yet
# affix colons on either side of $PATH to simplify matching
case ":${PATH}:" in
*:"$HOME/.local/bin":*)
;;
*)
# Prepending path in case a system-installed binary needs to be overridden
export PATH="$HOME/.local/bin:$PATH"
;;
esac
Upgrading uv(uvのアップグレード)
スタンドアロン インストーラーだとアップグレードも簡単だそうなので試してみましょう。
$ uv self update
info: Checking for updates...
success: You're on the latest version of uv (v0.7.2)
最新版ですね。スタンドアロンのインストーラーが、常に最新に追従してくれているのかも。
Shell autocompletion(シェルの自動補完)
コマンド入力時に便利になりそうなので、シェルの自動補完も手順通りに設定してみます。
$ echo 'eval "$(uv generate-shell-completion zsh)"' >> ~/.zshrc
$ echo 'eval "$(uvx --generate-shell-completion zsh)"' >> ~/.zshrc
そしてsource
で~/.zshrc
を実行します。
$ source ~/.zshrc
(eval):4549: command not found: compdef
(eval):140: command not found: compdef
あれ?compdef
ってなんだろう。
ちょっと調べてみたところ、Zshの公式サイト曰く自動補完を使うためにはシェル関数compinit
で初期化が必要とのこと。
To initialize the system, the function compinit should be in a directory mentioned in the fpath parameter, and should be autoloaded (‘autoload -U compinit’ is recommended), and then run simply as ‘compinit’. This will define a few utility functions, arrange for all the necessary shell functions to be autoloaded, and will then re-define all widgets that do completion to use the new system.
(Google翻訳)システムを初期化するには、関数compinit がfpathパラメータで指定されたディレクトリにあり、自動ロードされる必要があります (「autoload -U compinit」を推奨)。その後、単に「compinit」として実行されます。これにより、いくつかのユーティリティ関数が定義され、必要なすべてのシェル関数が自動ロードされるように調整され、新しいシステムを使用するために補完を行うすべてのウィジェットが再定義されます。
ふむふむ。そこで、~/.zshrc
の最初(手前で追加した内容より前ならいいはず)にautoload -U compinit
とcompinit
を追加しました。こんな感じです。
autoload -U compinit
compinit
. "$HOME/.local/bin/env"
eval "$(uv generate-shell-completion zsh)"
eval "$(uvx --generate-shell-completion zsh)"
そして、再びsource
で~/.zshrc
を実行します。
$ source ~/.zshrc
zsh compinit: insecure directories, run compaudit for list.
Ignore insecure directories and continue [y] or abort compinit [n]?
あれれ、今度はセキュアじゃないディレクトリがある?との警告が。再び公式サイトを確認すると、先ほどの続きにセキュリティチェックの解説もありました。
For security reasons compinit also checks if the completion system would use files not owned by root or by the current user, or files in directories that are world- or group-writable or that are not owned by root or by the current user. If such files or directories are found, compinit will ask if the completion system should really be used. To avoid these tests and make all files found be used without asking, use the option -u, and to make compinit silently ignore all insecure files and directories use the option -i. This security check is skipped entirely when the -C option is given, provided the dumpfile exists.
(Google翻訳)セキュリティ上の理由から、compinitは、補完システムが root または現在のユーザーが所有していないファイル、またはワールドまたはグループが書き込み可能なディレクトリ内、あるいは root または現在のユーザーが所有していないディレクトリ内のファイルを使用するかどうかも確認します。このようなファイルまたはディレクトリが見つかった場合、 compinit は補完システムを本当に使用すべきかどうかを確認します。これらのテストを回避し、見つかったすべてのファイルを確認なしで使用するには、オプション-uを使用します。また、安全でないファイルとディレクトリをすべてcompinitが無視するようにするには、オプション-iを使用します。ダンプファイルが存在する 場合、 -Cオプションが指定されると、このセキュリティチェックは完全にスキップされます。
なるほど、ディレクトリの権限がよくない模様です。エラーメッセージに従いcompaudit
すると対象ディレクトリを教えてくれました。
$ compaudit
There are insecure directories:
/usr/local/share/zsh/site-functions
/usr/local/share/zsh
2つあるので確認してみます。あ、ls
でディレクトリ調べる時は-d
が必要と。Linux初心者の私はこんなところでも時間が溶けていきます😅
$ ls -ld /usr/local/share/zsh/site-functions
drwxrwxr-x 4 ega admin 128 10 4 2020 /usr/local/share/zsh/site-functions
なるほど、所有者は自身の「ega」なのでいいのですが、グループに対する「w」(書き込み可能)が引っかかっている模様です。chmod g-w
で外してみましょう。
$ chmod g-w /usr/local/share/zsh/site-functions
$ ls -ld /usr/local/share/zsh/site-functions
drwxr-xr-x 4 ega admin 128 10 4 2020 /usr/local/share/zsh/site-functions
これでグループの「w」が取れました!
もう1つのディレクトリも確認します。
$ ls -ld /usr/local/share/zsh
drwxrwxr-x 3 ega admin 96 10 4 2020 /usr/local/share/zsh
こちらも同様なのでグループの「w」を外します。
$ ls -ld /usr/local/share/zsh
drwxr-xr-x 3 ega admin 96 10 4 2020 /usr/local/share/zsh
これで再びcompaudit
すると検出されなくなりました!
再び~/.zshrc
を実行します。
$ compaudit
$ source ~/.zshrc
お、エラーが出なくなりました!
これで、途中まで入力してTabを押すと補完してくれるようになりました!!
これでuvのインストールは完了です!
Pythonのインストール
続いて、公式の手順でPythonの最新版をインストールしてみます。
Installing Python(Pythonのインストール)
$ uv python install
warning: Failed to patch the install name of the dynamic library for /Users/ega/.local/share/uv/python/cpython-3.13.3-macos-aarch64-none/bin/python3.13. This may cause issues when building Python native extensions.
Installed Python 3.13.3 in 1.97s
+ cpython-3.13.3-macos-aarch64-none
あぅ、なんか警告が。あと、再び「コマンドラインデベロッパツール」のインストールを勧めるダイアログが出てきました。今回は「install_name_tool」を実行するために必要と言っています。
ちょっと調べてみたら、uvはインストール時にこのinstall_name_toolを使うそうで、コマンドラインデベロッパツールは入れておかないといけなかった模様です。うぅ、初めからコマンドラインデベロッパツールを入れておけばよかった😢
とりあえず、警告付きでインストールされたPythonは怖いので一度削除します。
$ uv python uninstall cpython-3.13.3-macos-aarch64-none
Searching for Python versions matching: cpython-3.13.3-macos-aarch64-none
Uninstalled Python 3.13.3 in 104ms
- cpython-3.13.3-macos-aarch64-none
なお、ここではバージョン指定の仕方がよくわからずインストール時に表示されたcpython-3.13.3-macos-aarch64-none
を指定してアンインストールしましたが、公式サイトのRequesting a versionに解説があり、バージョン番号だけでも指定できるそうです。
コマンドラインデベロッパツールのインストール
続いて、先ほどのコマンドラインデベロッパツールのダイアログで「インストール」します。ここはいつものMacのインストーラーなので簡単ですが、手元のM2 Mac miniでは10分ほどかかりました。
終わったら再び挑戦です。
Pythonのインストール再び
$ uv python install
Installed Python 3.13.3 in 2.52s
+ cpython-3.13.3-macos-aarch64-none
お、うまくいきました!
Viewing available Python versions(利用可能なPythonのバージョンを表示する)
使えるようになったPythonのバージョンを確認してみます。
$ uv python list --only-installed
cpython-3.13.3-macos-aarch64-none /Users/ega/.local/share/uv/python/cpython-3.13.3-macos-aarch64-none/bin/python3.13
cpython-3.9.6-macos-aarch64-none /usr/bin/python3
あれ、2つ出てきた。後者はuv
ディレクトリ下にいないので、どうやら先ほどのコマンドラインデベロッパツールで入ってしまったものみたいです。
管理対象外のものだけに絞ってみます。
$ uv python list --no-managed-python
cpython-3.9.6-macos-aarch64-none /usr/bin/python3
やっぱりそうですね。3.9.6はuvの管轄外になっています。どうせこうなるのなら、初めからコマンドラインデベロッパツールを入れておけばよかった(2回目)。
Creating a new project(新しいプロジェクトの作成)
公式の手順でプロジェクトを作ってみます。
$ uv init hello-world
Initialized project `hello-world` at `/Users/ega/projects/hello-world`
$ cd hello-world
$ uv run main.py
Using CPython 3.13.3
Creating virtual environment at: .venv
Hello from hello-world!
お、Venvの環境を自動で作ってくれた!activateもしてくれてるのかも?
$ echo $VIRTUAL_ENV
$ python3 --version
Python 3.9.6
どうやら、今のシェル内で切り替わる訳ではなさそう。
$ uv run python3 --version
Python 3.13.3
なるほど、あくまでもuvコマンド経由での世界ということですね。
とりあえずPythonのインストールまではできたので、今日はここまで。
前回の続きで、公式の説明を見ながらプロジェクトでの使い方を確認します。
Project structure(プロジェクト構造)
前回のuv init
でファイルが一式できています。
別で勉強を進めていたubuntuではpyenv+venv+pipを使っていましたが、それがuvだけで済む形です。
Managing dependencies(依存関係の管理)
uv add {パッケージ}
すると、インストールしてpyproject.toml
とuv.lock
も更新してくれます。
削除はuv remove {パッケージ}
ですね。
また、uv lock --upgrade-package {パッケージ}
すると他のパッケージはそのままキープしつつ、それらと互換のある一番新しいバージョンに更新してくれるそうです。便利!
Running commands(コマンドの実行)
uv run {スクリプトやコマンド}
で実行すれば、環境をpyproject.toml
とuv.lock
の内容に合わせてから実行してくれるそうです。つまり、pyproject.toml
とuv.lock
の情報に現状があってなければパッケージのインストールなどを自動的に実行してくれるわけですね。
なお、以下の実行例で--
という謎のオプション?があって調べてみたら、これは「以降はオプションはない」ことを示す慣習だそうです。つまり、以降はuvではなくflaskのオプションとして解釈される訳ですね。
uv add flask
uv run -- flask run -p 3000
uv run
は他にもいろいろなオプションがあり、以下のページで解説されています。
また、uv sync
で、pyproject.toml
とuv.lock
の内容に合わせてPythonのインストールやパッケージのインストールなどをやってくれます。uv run
を使っていれば不要みたいですが、これまでどおりコマンドを実行したい場合は、uv sync
後にvenvのアクティベートをすればOKです。
uv sync
source .venv/bin/activate
flask run -p 3000
python example.py
Building distributions(ディストリビューションの構築)
これはパッケージを配布する際の機能ですね。今は使わないのでスルーします。
これで、ざっくり必要最低限のことがわかりました。
なお、以下にあるスクリプトの実行やツールの使い方などは読み飛ばしているので、また使う時に戻ってこようと思います。
uv runで実行時の引数の解釈について
前回、コマンドラインの引数で--
というのが出てきて「以降はオプションはない」ことを示す慣習とのことでした。
uv run -- flask run -p 3000
でも、今勉強中の教材で、
uv run python -m app.hoge --user "fugo"
みたいなのが出てきて、--
がないので-m
以降もuv run
のオプションとして解釈されるのかと思ったのですが、uv run
にオプションが見つからず、調べてみたら誤解していたことがわかりました。
Arguments following the command (or script) are not interpreted as arguments to uv. All options to uv must be provided before the command, e.g., uv run --verbose foo. A -- can be used to separate the command from uv options for clarity, e.g., uv run --python 3.12 -- python.
(Google翻訳)コマンド(またはスクリプト)に続く引数は、uvの引数として解釈されません。uvのオプションはすべてコマンドの前に指定する必要があります(例: )uv run --verbose foo。--コマンドとuvのオプションを区別するために、 を使用できます(例:uv run --python 3.12 -- python)。
Google翻訳だと--
などが変な位置になってしまいますが、もともと--
がなくてもuv run
以降はuv
の引数にはならず、すべてその後に続くコマンドやスクリプトへの引数になるとのことです。ただ、わかりやすさのために--
を入れてもいいですよとのことで、前回の--
はわかりやすさのために入っていたことがわかりました。
今回の場合は以下の感じですね。
uv run python -m app.hoge --user "fugo"
引数 | 意味 |
---|---|
python |
uv run の実行対象。以降は-- がなくてもuv の引数とは解釈されない。 |
-m |
pythonのオプションで、指定されたモジュール(app.hoge )をスクリプトとして実行する。 |
app.hoge |
スクリプトとして実行するモジュール名。 |
--user "fugo" |
モジュール名より後ろはそのままモジュールに渡る引数。 |
わかりやすさの配慮の有無で混乱してわかりにくくなっていた訳ですね😅