LoginSignup
2
2

More than 1 year has passed since last update.

venv を condaっぽく使う

Last updated at Posted at 2021-08-27

長いことPython環境の管理には conda のお世話になってきたのですが、最近は venv が主流になりつつあるそうです。
主たる理由は標準ライブラリに組み込まれたこと、それから依存関係で不具合を起こしやすかった機械学習ライブラリの多くが、pip install で十分解決できるようになってきたことかと思います。

そこで、一念発起してvenvへの意向を試みたのですが、condaとの微妙なシンタックスの違いで慣れない部分がありました。
特に、

python -m venv env/path/name       # 任意の場所に仮想環境を作れる
source env/path/name/bin/activate  # アクティブ化するときはパスを入力する 

のように、アクティブ化するときにパス名を入力するのが面倒だな、と思いました。
condaだと、仮想環境はすべてminiconda/envs 内に作られ、どこからでも conda activate <envname>で使えます。
プロジェクトフォルダ内に環境を作っていれば良いのですが、汎用的な環境を使い回す場面も多いので、小回りがほしいと思いました。
そういうわけで、venvcondaっぽく使う方法について考えました。

tl;dr

下記のシェルスクリプトを .bashrc.bash_profileなど、端末開始時に読み込まれるファイルに追加することで、

  1. venvc <envname> で仮想環境が作られます。実体のフォルダは $VENVROOT 以下に作られます。
  2. venvc2, venvc3 でPythonのバージョンを選べます。
  3. venva ` で仮想環境をアクティブにします。
  4. venvd で仮想環境を非アクティブ化します。deactivateと同じです。
  5. venvr <envname> で仮想環境を削除します。つまり、実体のフォルダを消します。
  6. venvl で仮想環境の一覧を表示します。つまり、$VENVROOT直下のフォルダを表示します。
  7. 通常通り venvコマンドを使って任意の場所に仮想環境を作ることには干渉しません。
.bashrc,.bash_profileなど
# choose the default venv location
VENVROOT="$HOME/venv"

function _venvc() {
  # internally used by venv3 and venv2
  # $1 : env name
  # $2 : python version
  if [ -z "$1" ]
  then
    echo "Provide env name; e.g. venvc$2 <envname>"
  else
    python$2 -m venv "$VENVROOT/$1" && echo "Created env at $VENVROOT/$1"
  fi
}

function venvc2() { _venvc "$1" 2; }
function venvc3() { _venvc "$1" 3; }
alias venvc=venvc3
# because i use python3 most of the time

function venva() { source "$VENVROOT/$1/bin/activate"; }
alias venvd=deactivate

function venvr() { 
  # $1   : env name
  # $2...: options to rm, in case -f or sort is needed
  envname="$1" 
  shift 
  if [ -z "$envname" ]
  then
    echo "Provide env name; e.g. vencr <envname>"
  else
    rm -r $@ "$VENVROOT/$envname" &&  echo "Removed $VENVROOT/$envname"
  fi
}

function venvl() {
  # $1...: options to ls
  ls $@ "$VENVROOT"
}

詳しい説明

venvの通常の使い方

仮想環境の作成

venvを用いて仮想環境を作るには、次のコマンドを実行します。

$ python3 -m venv myenv
# ここで python3としているのは、多くのOSでデフォルトのpythonが python2になっていることが多いためです
# python2 や python にすると、対応するバージョンをベースにした仮想環境になります。

すると、カレントディレクトリ内に、myenvというフォルダが作られ、そのなかに仮想環境関係のファイルが作られます。確認してみます。

$ ls myenv/bin -l

#-rw-r--r-- 1 user user 2189  8月 28 07:23 activate
#-rw-r--r-- 1 user user 1241  8月 28 07:23 activate.csh
#-rw-r--r-- 1 user user 2393  8月 28 07:23 activate.fish
#-rw-r--r-- 1 user user 8834  8月 28 07:23 Activate.ps1
#-rwxrwxr-x 1 user user  254  8月 28 07:23 easy_install
#-rwxrwxr-x 1 user user  254  8月 28 07:23 easy_install-3.8
#-rwxrwxr-x 1 user user  245  8月 28 07:23 pip
#-rwxrwxr-x 1 user user  245  8月 28 07:23 pip3
#-rwxrwxr-x 1 user user  245  8月 28 07:23 pip3.8
#lrwxrwxrwx 1 user user    7  8月 28 07:23 python -> python3
#lrwxrwxrwx 1 user user   16  8月 28 07:23 python3 -> /usr/bin/python3
  • myenv/bin/python -> myenv/bin/python3 -> /usr/bin/python3 というようにシンボリックリンクが貼られています。つまり、この環境においてpythonを実行すると、/usr/bin/python3が実行されるということです。
  • pip はシンボリックリンクではないようです。サイズが小さいからでしょうか。

仮想環境の利用

作成した仮想環境を利用するには、次のコマンドを実行します。

$ source myenv/bin/activate

試しに pythonのバージョンを確認してみます。

(myenv) $ python -V
#Python 3.8.10

(myenv) $ which python
# /***/***/myenv/bin/python

確かに python3が使われ、また、コマンドは仮想環境内のシンボリックリンクを指しているようです。
つまり、これは実行ファイルの探索パスが変わっているということです。調べてみます。

(myenv) $ echo "$PATH"
# /***/***/myenv/bin:********

確かに、仮想環境内のbin/フォルダが最初に探索される場所になっています。これにより、仮想環境内のPythonが優先的に使われるようになります。

仮想環境の主たる目的はパッケージ群の管理なので、仕組みが気になります。これも確認しておきます。

(myenv) $ python -c "import sys; print(sys.path)"
#['', '/usr/lib/python38.zip', '/usr/lib/python3.8',
# '/usr/lib/python3.8/lib-dynload', '/***/***/myenv/lib/python3.8/site-packages']

最初にローカルフォルダ、次にグローバルなパッケージ、最後に仮想環境内のパッケージ、という順になっています。ということは、グローバル環境をベースに、それに仮想環境内でパッケージを追加するイメージのようです。

仮想環境の非アクティブ化

仮想環境の利用を終了するには、次のコマンドです。

(myenv) $ deactivate

$ python -V
#Python 2.7.18

非アクティブ化すると (myenv)の文字が消え、 Pythonのバージョンもデフォルトのものに戻りました。

仮想環境を削除する

特に削除コマンドは用意されていないようです。仮想環境の実体はフォルダに過ぎないので、これを削除することで替えるのが現状のようです。
場合によっては、-fオプションが必要なこともあるかもしれません。

$ rm -r myenv
# or
$ rm -rf myenv

condaっぽくする方法

venvによる作業はコマンドの実行すぎないので、それをラッピングすればcondaっぽくできそうです。
つまり、共通のルートディレクトリ(VENVROOT=$HOME/venv としている部分)を指定し、その中に仮想環境を作るようコード化すればよいです。

少し考慮が必要なのが、アクティブ化の処理です。

$ source myenv/bin/activate

この sourceというのは、実行シェルと同じ環境でスクリプトを実行することを意味します。こうしている理由は、$PATH変数など、実行元の環境への変更を伴う作業だからです。そのため、

$ bash myenv/bin/activate

では目的は達成されません。bashから呼ぶと、スクリプトは別プロセスで実行されるので、その途中で起こった変数変更は元の環境へは影響しないためです。

ここで行いたいことは、venva <envname> とすると、 source $VENVROOT/<envname>/bin/activate が実行元のシェルで実行される、ということです。それを実現する1つの方法が、関数を作ることです。というのも、シェルスクリプトの関数は、同一シェルで実行されるからです。つまり、

function venva() {
  source "$VENVROOT/$1/bin/activate"
} 

という関数を作れば良いということです。
そこで、全てのコマンドをシェル関数にラッピングして、それを .bashrc に追加することで、端末で常に使える状態にしています。使っているとコマンドとの差違が見えませんが、ファイルの実体がないため、which venvaが何も返さないという特徴があります。
その他の実装については、シェルの関数を書いているだけなので説明を割愛をします。

2
2
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
2
2