Pythonの仮想環境venvをちゃんと理解する
はじめに
Python の venv をなんとなくで使ってたんですが、期待する Python バージョンや pip パッケージにならなくてモヤモヤしてたので、真面目に動きを確認しました。完全に理解したので共有です。
python -m venv .venv
で作られるもの
まず、venv で仮想環境作る前に、現状のどの python を使ってるかみてみます。
私の環境だと、pyenv を使ってるので、このパスに python があります。せっかくなので、バージョンを変更してみます。
% which python
/Users/optimisuke/.pyenv/shims/python
% python -V
Python 3.11.6
% pyenv local 3.12
% python -V
Python 3.12.0
% which python
/Users/optimisuke/.pyenv/shims/python
次に、venv 環境をつくります。
% python -m venv .venv
この時点では、使ってる python に変更はありません。
% which python
/Users/optimisuke/.pyenv/shims/python
作られたフォルダの bin をみてみます。
% ls -ltr .venv/bin
total 72
lrwxr-xr-x 1 optimisuke staff 44 Dec 16 23:12 python -> /Users/optimisuke/.pyenv/versions/3.12.0/bin/python
lrwxr-xr-x 1 optimisuke staff 6 Dec 16 23:12 python3 -> python
lrwxr-xr-x 1 optimisuke staff 6 Dec 16 23:12 python3.12 -> python
-rwxr-xr-x 1 optimisuke staff 251 Dec 16 23:12 pip
-rwxr-xr-x 1 optimisuke staff 251 Dec 16 23:12 pip3
-rwxr-xr-x 1 optimisuke staff 251 Dec 16 23:12 pip3.12
-rw-r--r-- 1 optimisuke staff 2210 Dec 16 23:12 activate.fish
-rw-r--r-- 1 optimisuke staff 931 Dec 16 23:12 activate.csh
-rw-r--r-- 1 optimisuke staff 9033 Dec 16 23:12 Activate.ps1
-rw-r--r-- 1 optimisuke staff 2350 Dec 16 23:12 activate
python のシンボリックリンク(エイリアス)があることがわかります。pyenv で入れた python の実態にリンクされてそうです。
python -m venv .venv
するタイミングで有効になってる python が使われそうです。
source .venv/bin/activate
したら実行されるもの
activate してみます。期待通りの python を実行できそうです。何が起きたのでしょう?
% source .venv/bin/activate
% which python
/Users/optimisuke/Repos/sandbox/gradio/.venv/bin/python
% python -V
Python 3.12.0
activate を見てみると、ただのシェルスクリプトです。大事なのはここだけです。作った.venv
へのパスを通してるだけです。
VIRTUAL_ENV="/Users/optimisuke/Repos/sandbox/gradio/.venv"
export VIRTUAL_ENV
_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/bin:$PATH"
export PATH
パスを見てみるとこんな感じです。先頭にパスが追加されるので、コマンドを打った時に最初に検索されます。python コマンド実行時に使われる実行ファイルが変わったのはこれだけでしたね。pip も同じように.venv/bin
にあるのでそれが使われます。
% echo $PATH
/Users/optimisuke/Repos/sandbox/gradio/.venv/bin:/Users/optimisuke/.deno/bin:/Users/optimisuke/.local/bin
vscode のインタープリタ候補に表示されるもの
右下に書かれてる python のバージョンが、たまに python コマンドのバージョンとずれたりします。vscode にどれを使っているか教えてあげないと lint とかもうまくいきません。デフォルトでは、ワークスペースも探してくれるみたいですが、ワークスペースにフォルダ作ってそっちで作業してるとうまくいきません。候補に出てこない場合は.venv
内のパスを指定して教えてあげる必要があります。
デフォルトで探すフォルダはsettings.json
で設定できるみたいです。
deactivate
したら実行されるもの
activate
ないにあった以下の関数が呼び出されます。いい感じにパスが通っているので、コマンドとして呼び出せます。大事なのは PATH の設定を戻しているところです。すごくシンプルですね。
deactivate () {
# reset old environment variables
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
PATH="${_OLD_VIRTUAL_PATH:-}"
export PATH
unset _OLD_VIRTUAL_PATH
fi
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
export PYTHONHOME
unset _OLD_VIRTUAL_PYTHONHOME
fi
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
hash -r 2> /dev/null
fi
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
PS1="${_OLD_VIRTUAL_PS1:-}"
export PS1
unset _OLD_VIRTUAL_PS1
fi
unset VIRTUAL_ENV
unset VIRTUAL_ENV_PROMPT
if [ ! "${1:-}" = "nondestructive" ] ; then
# Self destruct!
unset -f deactivate
fi
}
おわりに
以上が、venv の動きでした。調べてみると、ものすごくシンプルですね。
Discussion