🎉

【初心者向け】pipenv shell終了時の落とし穴:exitとdeactivateの違いを理解する

に公開

Pythonでの仮想環境に慣れておらず、仮想環境から抜け出す際に躓いたので備忘録です。
初心者向けです。

この記事でわかること

  • pipenvとvenvでの終了方法の違いとその理由

実際に起ったこと

  • pipenv shellで仮想環境を使用開始
  • deactivateを実行(これで仮想環境を終了したつもりだった)
  • 再度、仮想環境に入ろうとpipenv shellを実行すると以下のようになり、正しく起動できない。
    Shell for UNKNOWN_VIRTUAL_ENVIRONMENT already activated.
    New shell not activated to avoid nested environments.
    

解決方法

  • pipenvの場合はdeactivateではなく、exitコマンドで終了する

解決方法は単純でpipenvの場合は単にexitしてあげたら良かったです。
では、なぜvenvのようにdeactivateでは抜けれないのでしょうか?

pipenv shellの仕組み

サブシェルが仮想環境を使用している

  • pipenv shellをすると、新しいシェル(サブシェル)が起動される
  • サブシェル内で仮想環境のactivateスクリプトが実行され、PATH環境変数の先頭に仮想環境のパスが追加される
  • その結果、pythonコマンドを打つと、OS標準ではなく仮想環境のPythonが呼び出される

pipenvの場合はサブシェルが立ち上がっていたんですね。
なので、終了させる場合にはシェル自体を閉じる必要があり、exitを打つ必要がありました。

venvの仕組み

元のシェルで仮想環境を使用している

  • source {仮想環境名}/bin/activateで、仮想環境を使用開始
  • 現在のシェルのままでPATHに仮想環境のパスを追加

venvの場合は今いるシェルのままで、PATHを変更するなどの処理を行っています。
なので、ここでdeactivateとすることで仮想環境を使用するために行った変更が元に戻るので、あとは何も変わりなしというわけです。

お試しコーナー

実際に以下のようにコマンドを打ってみると、PATHが変わっていることがわかりますね

# まず仮想環境を立ち上げていない状態
$ echo $PATH
# 省略していますが、以下のようにPATHが表示されます
/Users/username/.pyenv/shims:/Users/username/~~~

# 次に仮想環境立ち上げた状態
$ pipenv shell # 仮想環境の立ち上げ
$ echo $PATH
# 一部内容は書き換えていますが、以下のようにPATHの先頭にvirtualenvsの設定が追加されていることがわかります
/Users/username/.local/share/virtualenvs/sample-project-hash/bin:/Users/username/.pyenv/shims:/~~~

上記のようにPATHが変わっていることがわかります。which pythonコマンドで現在使用中のPythonパスを確認するでもいいですね。

余談: サブシェル内で更にpipenv shellをすることも可能

色々と調べて試してみると、どうやらpipenv shellで起動したサブシェル内で更にpipenv shellすることもできることがわかりました。(特におすすめはしていません)

どうやら、pipenv shellをすると環境変数としてPIPENV_ACTIVEが設定されているようで、この設定の有無で二重で仮想環境を起動することを防いでいるようでした。

なので試しに以下のようにしてみると、仮想環境を起動することができました。
※ 理解を深めるための実験なので、実際に行うことは推奨しておりません。

Step1: 初期状態の確認

# シェルが変わっていることも確認のため、VSCodeで開いた状態のSHLVLを確認
$ echo $SHLVL
3 # VSCode内だとdefaultが3のようです

Step2: 初回のpipenv shell実行

# 初回の起動
$ pipenv shell
Launching subshell in virtual environment...
 source /Users/my-pc/.local/share/virtualenvs/test/bin/activate

# この時点でSHLVLが1つ増え、サブシェルが起動していることがわかる
$ echo $SHLVL
4

Step3: deactivate実行

# この状態でdeactivateすると、サブシェルからはでないが仮想環境の設定は一部消える
$ deactivate

# PIPENV_ACTIVATEは残っている
$ echo $PIPENV_ACTIVE
1

Step4: PIPENV_ACTIVATEの手動クリア

$ unset PIPENV_ACTIVE
# この状態でpipenvは「非アクティブ」と判断する

Step5: 再度pipenv_shellを実行

# この状態で再度pipenv shell
$ pipenv shell
Launching subshell in virtual environment...
 source /Users/my-pc/.local/share/virtualenvs/test/bin/activate # 再度起動している

# SHLVLを見ると更にサブシェルが起動されていることがわかる。(4→5になっている)
$ echo $SHLVL
5 # 多重ネストが発生

このようにすると、サブシェル内でもpipenv shellが実行できてしまいました。
これは意図せず実行していると良くない状態ですね。
こういったことを防ぐためにサブシェル内ではpipenv shellができないようになっているので、必ずexitするようにしましょう

終わりに

改めて整理すると、pipenvとvenvで終了する際は以下のようになります。

  • pipenv: exitで終了(サブシェルを閉じる)
  • venv: deactivateで終了(環境設定を元に戻す)
  • 違いの理由:実行方式(サブシェル vs 環境変数変更)が異なるため

今回仮想環境の仕組みについて調べる中で、副次的な学びを多く得ることができたので個人的に満足です。
業務をしている中ではこういった細部を調べる時間はないですが、余暇の時間で深ぼってみるのはいいですね。

Discussion