0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python仮想環境とシェルキャッシュの落とし穴

Last updated at Posted at 2025-03-27

はじめに

Pythonで開発を進める際、仮想環境を利用することで依存パッケージのバージョン管理やプロジェクト間の衝突を防ぐことができます。しかし、実際には「仮想環境を有効化しているはずなのに、なぜかグローバル環境のコマンドが実行されてしまう」というトラブルが発生することがあります。このため、他のメンバーのローカル環境では正しくコードが動くのに、自分のローカル環境だけで謎の(プログラム以外の)エラーが発生する場合もあります。

今回は、ローカル環境で以下のようなエラーに悩まされた事例をもとに、

  • 仮想環境とグローバル環境の混在
  • シェルのPATHとコマンドキャッシュの仕組み
    などの原因と、その解消方法について備忘録として記載します。

ローカル環境で発生したエラーとその原因

1. エラー発生の背景

プロジェクトの requirements.txt は以下のようになっていました。

streamlit
llama-index
openpyxl
...その他諸々...
docx2txt

上記のファイルをもとに、仮想環境(.venv)を作成し、以下のコマンドで依存関係をインストールしました。

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

その後、アプリを起動しようとすると以下のエラーが発生しました。

ModuleNotFoundError: No module named 'docx2txt'

しかし、pip show docx2txt を実行すると、バージョン0.8が表示され、確実にインストールされていることが確認できました。

2. 問題の検証

仮想環境の確認

  • 仮想環境は正しくアクティベートされており、プロンプトにも (venv) と表示されていました。

  • しかし、以下のコマンドで実行した結果、実際に使用されている streamlit コマンドがグローバルのものを指していました。

    which streamlit
    # 結果: /Library/Python/3.9/bin/streamlit
    

重要な発見:python -m streamlit run ...streamlit run ... の違い

  • python -m streamlit run src/backend/main.py
    → この形式で実行すると、アクティブな仮想環境内のPython が使われ、そのPythonにインストールされているモジュール(docx2txtも含む)を正しく参照します。
    → 結果、エラーは発生せず正常に動作しました。

  • streamlit run src/backend/main.py
    → この場合、シェルは $PATH を使って streamlit を探しますが、キャッシュの影響などでグローバルの streamlit が優先的に実行される場合があり、仮想環境内のモジュールが読み込まれずエラーが発生しました。
    → 私は以前からこちらで動作確認していたので、おそらくキャッシュが溜まってしまっていたのだと考えられます。

3. 原因の詳細:シェルの $PATH とキャッシュの仕組み

以下は、実際の例を交えて「$PATH の基本」と「仮想環境を有効化したときにどのように $PATH が変化するか」を説明した内容です。※ すでに基本的な知識はお持ちの方は読み飛ばしてください。

$PATH の基本

① シェルの動作例

  • $PATH は、シェルがコマンド(例:streamlit)を探すときに参照するディレクトリのリストです。
    たとえば、以下のコマンドを実行すると:

    echo $PATH
    

    と出力される内容が、$PATH の一覧です。
    例:

    /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
    
  • この場合、シェルはコマンドを探すとき、最初に /usr/local/bin を確認し、次に /usr/bin、その後 /bin の順で探します。
    たとえば、以下のコマンドを入力した場合:

    streamlit run app.py
    

    シェルはまず /usr/local/binstreamlit があるか調べ、あればそれを実行します。

② 具体例

  • 例1:
    /usr/local/binstreamlit が存在していた場合、そのコマンドが実行されます。
  • 例2:
    /usr/local/binstreamlit がなければ、次に /usr/bin を探して実行します。

仮想環境を有効化すると $PATH にどのような変化が起きるか

① 仮想環境の作成と有効化

  • プロジェクトディレクトリで以下のコマンドを実行して仮想環境を作成し、アクティベートします。

    python -m venv .venv
    source .venv/bin/activate
    
  • これにより、シェルは自動的に $PATH の先頭に .venv/bin を追加します。
    例えば、アクティベート前の $PATH が:

    /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
    

    であったとすると、アクティベート後は:

    /Users/ユーザ名/プロジェクトパス/.venv/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
    

    のように変化します。

② 具体例

  • 仮想環境を有効化後、which streamlit を実行すると、
    出力例は:
    /Users/あなたのユーザ名/プロジェクトパス/.venv/bin/streamlit
    
    となり、仮想環境内の streamlit が優先されることが確認できます。

シェルのコマンドキャッシュについて

  • シェル(bash や zsh)は、一度見つけたコマンドのパスを内部キャッシュに保存します。 これにより、同じコマンドを次に実行するとき、すぐにそのパスを返すようになっています。
  • しかし、このキャッシュが古い情報のまま残っていると、新たに仮想環境内にインストールされたコマンドが反映されず、以前キャッシュされたグローバル環境のパスが使われ続ける可能性があります。

私自身、仮想環境をアクティベートすれば必ず仮想環境内のコマンドが使われると思い込んでいたため、エラーの罠にハマりました。つまり、仮想環境に入っていても、シェルのキャッシュがグローバルのコマンドを記憶していると、実際にはグローバルのコマンドが実行されてしまうのです!
この仕組みは一見便利ですが、状況によっては非常に不便な結果を招くため、キャッシュが問題となることを忘れないようにしましょう。

  • この場合、rehash(または hash -r)を実行してキャッシュをクリアすることで、最新の $PATH に基づいてコマンドが探し直されます。

まとめ

  • $PATH はシェルがコマンドを探すディレクトリリストで、先頭から順にチェックします。
  • 仮想環境を有効化すると、その環境の bin ディレクトリが $PATH の先頭に追加され、仮想環境内のコマンドが優先されます。
  • しかし、シェルは一度見つけたコマンドのパスをキャッシュするため、場合によっては古い(グローバルの)パスが使用されることがあります。
    この場合は rehash などでキャッシュをクリアする必要があります。

それでも解決できない場合は

・強制的に仮想環境を抜ける
一旦新しいシェルを開く、または以下のように仮想環境の設定をスキップして新しいセッションを開いてください:

exec zsh

おわりに

今回の経験から得られた最大の教訓は、仮想環境を有効化するだけではなく、実際にどのPythonコマンドやモジュールが呼び出されているかを常に確認することの重要性です。
シェルの$PATHやキャッシュの仕組みを理解することで、環境トラブルの原因を早期に特定し、解決に導くことができます。

この内容が皆さんのトラブルシューティングの参考になれば幸いです。
ぜひコメントやフィードバックをお寄せください

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?