【Python】仮想環境(venv)の仕組みを動作の流れから理解する
はじめに
以前の記事で、仮想環境についての基本的な知識と、使用する理由についてまとめました。
(参考: https://qiita.com/mabo23/items/c2cb995a98d7003d7bdd)
この記事では、実際にPython標準の仮想環境作成モジュール「venv」使って仮想環境を構築する手順について、実際の動作をイメージしやすいよう、仕組みについて言及しながら解説していきます。
※ なお、この記事は仮想環境の基本と使用理由について押さえている方が対象です。もし、まだ理解できていないという方は、上記の記事を参考にしていただけますと幸いです。
※ また、「シェルとプロセスの関係」について押さえておくことでより正しく処理の流れを理解することができます。シェルとプロセスの関係について、以下の記事にまとめていますので、よろしければ参考にしてみてください。
(参考: https://qiita.com/mabo23/items/45b4838050a95903d10e)
※ 本記事では、Mac や Linux など Unix 系システムでの動作を前提としています。
Windows ユーザの方は、PowerShell や .bat
ファイルを使った activate 方法が異なるため、適宜読み替えてください。
本記事で理解しておきたいポイント
- 仮想環境の基本概念と必要性
-
venv
を使った仮想環境の作成と有効化・無効化の手順 -
PATH
などの環境変数が果たす役割 -
fork → exec
によるプロセス実行の流れ -
source
などのビルトインコマンドと外部コマンドの違い
仮想環境について簡単におさらい
仮想環境とは、あるプロジェクト専用のPython実行環境を、自分のPCの中に一時的に用意する仕組みのことです。これを使うことで、複数のプロジェクトごとに異なるライブラリやバージョンのPythonを共存させることができます。
※https://www.tutorialspoint.com/python/python_virtual_environment.htm より画像引用
Pythonでは、外部ライブラリの多くを pip install
によってインストールして使用します。
もし仮想環境を使わず、グローバル環境(OS全体で共通のPython)に直接インストールしてしまうと、他のプロジェクトとライブラリのバージョンが衝突してしまい、動かなくなってしまうことがあります。
例えば、あるプロジェクトでは Django 3.2 が必要で、別のプロジェクトでは Django 4.0 が必要だとしましょう。これを同時にグローバル環境に共存させることはできません。
しかし、仮想環境を使えば、各プロジェクトごとに別々のDjangoを自由にインストールでき、互いに干渉しないようにできます。
このように仮想環境は、プロジェクトごとの依存関係をきちんと分離するための土台となる非常に重要な仕組みです。
仮想環境の作成手順
-
ディレクトリを作る
$ mkdir myapp
$ cd myapp
-
仮想環境を作る(最初の1回だけ)
$ python3 -m venv venv
-
仮想環境を有効化(毎回の作業時)
$ source venv/bin/activate
-
プログラムを書く・pip installする
-
必要に応じて作業が終わったら無効化
$ deactivate
1. ディレクトリを作る
まずは、ターミナルやコマンドプロンプトで、仮想環境を置きたいディレクトリに移動しましょう。
プロジェクト用のフォルダがまだない場合は、作成しておくとわかりやすいです。
$ mkdir project # ディレクトリの作成
$ cd project # 当該ディレクトリに移動
2. venvによる仮想環境の作成
Python 3.3以降には、標準ライブラリとして venv モジュールが搭載されており、追加のツールを入れずに仮想環境を簡単に作成することができます。
python3 -m venv venv # 仮想環境 venv の作成
各コマンドの意味
-
python3
: Pythonインタプリタを呼び出す
※「インストールされている Python 3.x の実行環境を起動する」という意味 -
-m
: Python モジュールをスクリプトとして実行するためのオプション
例えばpython3 -m venv
は「venv というモジュールの中にある処理を実行せよ」という意味になります。 -
venv
(1つ目): 使用するモジュール名
venv.py という Python ファイル(もしくはパッケージ)がモジュールです。 -
venv
(2つ目): 作成するディレクトリ名
ここで指定された名前のディレクトリに、仮想環境の構成(Python実行ファイル・ライブラリのコピー・activateスクリプトなど)が生成されます。
ここでは任意のディレクトリ名をつけることができます。
このコマンドによって、venv
という名前のフォルダが作られ、その中にPythonの実行ファイルやパッケージ管理に必要な仕組み一式が構成されます。macOSやLinuxでは bin/
ディレクトリが作られ、その中に python
や pip
などの実行ファイルが配置されます。
venv/
ディレクトリ構成(例)
仮想環境 venv/
├── bin/ # 仮想環境を操作するための実行ファイルやスクリプトがを格納
│ ├── activate # 仮想環境を有効化するためのシェルスクリプト
│ ├── activate.csh
│ ├── activate.fish
│ ├── pip
│ ├── pip3 # 仮想環境専用のpipコマンド
│ ├── python
│ └── python3 # 仮想環境専用のPython実行ファイル
├── include/ # Python C拡張をビルドするときに使うCヘッダファイルのためのディレクトリ
├── lib/ # 仮想環境が使う Python標準ライブラリや追加ライブラリ(site-packages) を保存する場所
│ └── python3.X/
│ └── site-packages/ # pip install したライブラリの保存先
└── pyvenv.cfg # 仮想環境の設定ファイル
この仮想環境は、実際にはグローバルにインストールされているPythonの実行ファイルをコピーまたはリンクする形で作られますが、内部的に仮想環境用に分離されたライブラリの検索パス(site-packages)を使うようになっています。
3. 仮想環境の有効化
仮想環境を作成しただけでは、まだその環境は有効になっていません。仮想環境を使って開発を始めるには、まずその環境を「有効化(activate)」する必要があります。
仮想環境を有効化するため、以下のコマンドをターミナルで実行します。
$ source venv/bin/activate
このコマンドを実行することにより、現在のシェルプロセスに仮想環境が反映されるようになります。
※ Bash などの一部のシェルでは、source
は .
(ドット)と同義です
したがって、以下のコマンドも有効です。
$ . venv/bin/activate
有効化に成功すると、プロンプトの先頭に (venv)
のような表示が付きます。
これは、現在のシェルセッションが仮想環境に切り替わっていることを意味します。
この状態で python3
と入力すると、仮想環境内の Python 実行ファイルが使われます。同様に pip3 を使えば、インストールされるパッケージはすべてこの仮想環境専用の領域(venv/lib/.../site-packages
)に保存されるようになります。
つまり、この状態ではグローバル環境を一切汚さずにライブラリを自由に追加・削除することが可能になるわけです。
# 一部省略
venv/
├── bin/ # 仮想環境を操作するための実行ファイルやスクリプトがを格納
│ ├── activate # 仮想環境を有効化するためのシェルスクリプト
│ ├── pip3 # 仮想環境専用のpipコマンド
│ └── python3 # 仮想環境専用のPython実行ファイル
├── lib/
│ └── python3.X/
│ └── site-packages/ # pip install したライブラリの保存先
さらに、この動作を理解するためには、
① そもそもsource
コマンドが何か
② なぜこのコマンドを実行することで仮想環境が有効になるのか
を知る必要があります。
3-1. source コマンド
source
コマンドは、主にシェルスクリプトや設定ファイルを現在のシェル環境で読み込んで実行するためのコマンドです。
$ source ファイル名 or ファイルパス
通常、スクリプトを実行すると、新しいシェルプロセスが始まり、その中で処理がされます。
通常のスクリプト実行
※ 筆者の手書きメモ
source
コマンドを使用することで「現在のシェル」内でファイルの読み込み・実行を行ないます。
※ source
コマンドはビルトインコマンドで、新たなプロセスで実行されるわけではないので、注意しましょう
source
)による実行
ビルトインコマンド(
※ 筆者の手書きメモ
なお、ビルトインコマンドと外部コマンド(バイナリファイル・スクリプトファイル)については、よろしければ以下の記事をご参照ください。
(参考: https://qiita.com/mabo23/items/8bf73985946c541572c4)
ここで、仮想環境を有効化するためのコマンドをもう一度見てみましょう。
$ source venv/bin/activate
これは、先ほど紹介した venv ディレクトリ構成例を元に一部抜粋すると、以下のようになります。
venv/
├── bin/ # 仮想環境を操作するための実行ファイルやスクリプトがを格納
│ ├── activate # 仮想環境を有効化するためのシェルスクリプト
. .
. .
. .
つまり、venv フォルダの中にある activate ファイル(シェルスクリプト)を現在のシェルプロセスに読み込んでいます。
これによって、activate ファイルに記述されたシェルスクリプトが現在のプロセスに反映されます。
ちなみに、この activate ファイルは、venv モジュールを使って仮想環境を作成するときに自動的に作成されるシェルスクリプトファイルです。
そのため、$ source venv/bin/activate
によってスクリプトファイルが実行されます。
3-2. なぜ activate で仮想環境になるのか = 環境変数の仕組み
activate は普通のシェルスクリプトファイルです。通常のシェルスクリプト同様、source
コマンドを実行することで、スクリプトファイルの内容を現在のシェルプロセスに反映されます。
この activate というスクリプトファイルを実行することで、PATHという環境変数に変化があります。
PATHの先頭に venv/bin
が置かれるということが環境変数内部で行われます。
具体的には以下のような処理が行われます。
3-2-1. PATH の変更
PATH 環境変数の先頭に、仮想環境内の bin/
ディレクトリが追加されます。
これにより、同名のコマンド(例えば python3
や pip3
のような bin/
ディレクトリは以下のディレクトリ)が仮想環境内のものを優先的に使うようになります。
_OLD_VIRTUAL_PATH="$PATH"
VIRTUAL_ENV="/home/user/project/venv"
PATH="$VIRTUAL_ENV/bin:$PATH"
export PATH
新たなPATH変数は以下の順番でディレクトリを探索することになります。
# PATH変更前
/usr/local/bin:/usr/bin:/bin:...
# PATH変更後
/home/user/project/venv/bin:/usr/local/bin:/usr/bin:/bin:...
これにより、以後の python3
や pip3
コマンドは、グローバル環境の /usr/bin/python3
ではなく、venv/bin/python3
を指すようになります。
VIRTUAL_ENV
環境変数の定義
3-2-2. 現在どの仮想環境が有効になっているかを示すため VIRTUAL_ENV
という環境変数が設定されます。
この変数は deactivate するときにも使われます。
VIRTUAL_ENV="/home/user/project/venv"
export VIRTUAL_ENV
3-2-3. シェルプロンプトの変更(任意)
仮想環境が有効であることを視覚的にわかりやすくするため、プロンプト(PS1)に仮想環境の名前が追加されます。
PS1="(`basename \"$VIRTUAL_ENV\"`) $PS1"
3-2-4. deactivate 関数の定義
仮想環境を終了させるための関数 deactivate がシェルに追加されます。
この関数が呼び出されると、PATH や PS1 の変更が元に戻り、仮想環境が無効になります。
deactivate () {
# 元の PATH を復元
PATH="$_OLD_VIRTUAL_PATH"
export PATH
# プロンプトを戻す(あれば)
PS1="$_OLD_VIRTUAL_PS1"
export PS1
# その他のクリーンアップ
unset VIRTUAL_ENV
unset deactivate
}
これらの一連の処理によって、activate ファイルが読み込まれ、仮想環境が有効化します。
4. 仮想環境内で作業
仮想環境を有効化し、python3 スクリプトファイル
を実行すると、PATH の設定により仮想環境内の python3 実行ファイル
(Pythonインタプリタ)が使用され、それが新たなプロセスとしてメモリにロードされ、スクリプトを実行していきます。
※ 筆者の手書きメモ
仮想環境を有効化すると、PATH の先頭に仮想環境の bin/
ディレクトリが追加されるため、
python3
コマンドを打ったときに 仮想環境内の python3 実行ファイル
(実体はシンボリックリンクや実行ファイル)を使うようになります。
そして、python3 スクリプトファイル.py
を実行すると、
- その仮想環境の
python3 実行ファイル
がプロセスとして起動され、 - 指定されたスクリプトファイルを読み込み、
- Pythonインタプリタがそのスクリプトを解釈・実行します
venv/
├── bin/
│ ├── activate
│ ├── pip3
│ └── python3 # 仮想環境専用のPython実行ファイル(Pythonインタプリタ)
├── lib/
│ └── python3.X/
│ └── site-packages/
└── pyvenv.cfg
このときにロードされる Python 実行ファイルは、システム全体ではなく仮想環境用にインストールされたものです。
また、仮想環境を有効化している間に外部かインストールしたパッケージ(ファイル)は lib/
配下の site-packages/
に保存されます。
5. deactivate
deactivate は仮想環境を使わない状態に戻すための処理です。
現在使用している仮想環境を修了するために以下のコマンドを実行します。
$ deactivate
これにより、仮想環境は消えないが使わない状態にすることができます。
# deactivate 前
/home/user/project/venv/bin:/usr/local/bin:/usr/bin:/bin:...
# deactivate 後
/usr/local/bin:/usr/bin:/bin:...
仮想環境そのものはディレクトリとして残っているため、次回以降も同じように
$ source venv/bin/activate
を実行することで再び利用できます。
まとめ
- ストレージ上:
venv/
フォルダ群として存在する - シェル環境上:PATH を変更することで仮想環境を「使う」状態にする
- メモリ上:python を実行したときに、仮想環境の子プロセスがメモリに展開される
この仕組みを理解すると、仮想環境はプロセスの中に埋め込まれているものではなく、ストレージ上の構成とシェル環境変数の操作で成り立っていることがわかります。
また、仮想環境を有効にしている間も、プロセスの実行そのものは常に「fork→exec」の原則に従って行われていることがわかります。
Python仮想環境の操作は、一見単純に見えますが、実際にはOSとシェル、ファイルシステム、プロセス、環境変数といった複数の概念が密接に連動しています。
仮想環境を深く理解するには、単に venv フォルダがあるだけでなく、「どうやってその実行環境がメモリ上に展開されるのか」という視点を持つことが大切です。
本記事で紹介した「venv」を使った仮想環境の作成と運用の流れを、自分の手元のプロジェクトでぜひ実践してみてください。
最後までお読みいただき、ありがとうございました。
参考・画像引用元URL
※ ディレクトリ構成の例はChatGPTより引用
Discussion