venv+VS Codeでプロンプトが変な状態になったときの修復法

2024.06.05

はじめに

VS Codeのターミナル+venvでPythonの仮想環境を作る際に、特定条件下でプロンプトが変な状態になることがあります。普通に困ったので備忘録として記録しておきます。

再現を確認できたのは、以下の環境です。

OS VS Code version Python version
macOS Sonoma14.5 1.89.0 (Universal) Python 3.12.3

変な状態ってなによ

再現手順 (修復できますが、自己責任でお願いします。)

  • 普通にvenv環境を作る
  • VS Codeのターミナル上でsource activateして仮想環境を有効化 (行頭に(環境名)が付きます)
  • いったんVS Codeのウインドウを×で閉じる
  • VS Codeを開き直す
  • ターミナルの履歴が復元された場合、venvの仮想環境が有効になったままだが、この状態でdeactivateが実行できない。
  • PATHとかもズレたままになるので詰む
(venv) user sample_app % deactivate         
(venv) user sample_app %

最終的な状況はこんな感じになります。表示上は仮想環境にいるのにdeactivateは使えません。エラーが出ることもあれば出ないこともある気がします。 裏側で何が行われているのか、次の節で調べてみます。お急ぎの方は読み飛ばしてください。

状況

activateは簡単にいうとこんなことをやっています。

1.deactivate関数を作る

deactivate () {
    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

    hash -r 2> /dev/null

    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
        unset -f deactivate
    fi
}

この関数は後述する各環境変数の変更を元に戻します。

2.deactivateする

deactivate nondestructive

すでにvenv環境が有効ならdeactivateします。ただし、nondestructiveを引数に与えているのでdeactivate関数自体は破棄しません。

3.環境変数を上書きする

if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then
    export VIRTUAL_ENV=$(cygpath "/Users/user/sample_app")
else
    export VIRTUAL_ENV="/Users/user/sample_app"
fi

_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/bin:$PATH"i
export PATH

if [ -n "${PYTHONHOME:-}" ] ; then
    _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
    unset PYTHONHOME
fi

if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
    _OLD_VIRTUAL_PS1="${PS1:-}"
    PS1="(sample_app) ${PS1:-}"
    export PS1
    VIRTUAL_ENV_PROMPT="(sample_app) "
    export VIRTUAL_ENV_PROMPT
fi

hash -r 2> /dev/null

ここでやっていることは、

  • $PATHをバックアップの上あらかじめ定義されているもので置き換え
  • $PYTHONHOMEをバックアップの上削除
  • $PS1に(環境名) を付ける

ほかにもいろいろやってますが、今回影響してくるのはここら辺かなと思います。$PYTHONHOMEに関しては、そもそも値が入っていないこともあります。

で、今回の変な状況というのがどういう状態になっているかというと、

  • activateでPATHとPS1がバックアップ&上書きされる
  • VS Codeを×で閉じて履歴が復元されると、exportした環境変数が消えない!!
  • exportされた上書き後のPS1などは残っているのに、元の_OLD_xxxが消えてしまっているので、deactivate内のif文に引っかからず実行できない。

という感じっぽいです。困った。。。

解決方法

解決方法はとても簡単です。影響が出ている環境変数が少なくとも、

  • $PATH
  • $PS1

であることがコードから自明です。人によっては$PYTHONHOMEも。

そのため、別のターミナルなどで上の環境変数をechoし、これで上書きすればOKです!。頻発する場合はzshrcとかで対応してもいいと思います。

終わりに

deactivateせずに閉じちゃったりすると引っかかることがあるので結構厄介な気がしています。