🐍

Pythonの仮想環境venvをちゃんと理解する

2023/12/16に公開

はじめに

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で設定できるみたいです。

https://tekunabe.hatenablog.jp/entry/2018/12/28/vscode_venv_default_rolad

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 の動きでした。調べてみると、ものすごくシンプルですね。

GitHubで編集を提案

Discussion