direnvでAnacondaとシステムのPythonを分離する

# direnvとconda利用者の悩み
direnvは現在いるディレクトリに応じて環境変数を切り替えてくれるツールである。これを使うとAnaconda利用者の悩みであった、システムのPythonなどがAnacondaのものでPathが隠されてしまうことが回避できる。
これまでAnacondaを利用する際にはこのPathの無差別な覆いを防ぐために、pyenvなどを通してCondaを利用することが多かった。一方でpyenvはそもそもPythonの複数バージョンを管理するツールで、インストールしたpythonバイナリ自体を管理されてしまい、よくわからない挙動をすることも多く、扱いにくいところもあった。

[Qiita: pyenvが必要かどうかフローチャート](https://qiita.com/shibukawa/items/0daab479a2fd2cb8a0e7)
[pyenv経由でAnacondaをインストールするメリット](https://teratail.com/questions/74325)

condaの利用者がみんなよってたかってpyenvを利用するようになった結果、condaもpyenvもいらないような人が巻き込まれたりしたようだ。Anacondaの公式サイトではインストールの後に、activate/deactivateでpathの問題は切り替えろ！と書いてあり、そんなことしてられるか！という感じである。先日JuliaTokyoに参加した際に、@bicycle1885さんがdirenvというツールがあることを教えてくれた。

[講演のスライド: 覇権を取るパッケージ作成 佐藤健太@bicycle1885](https://docs.google.com/presentation/d/1xNPjtt7WBS2Lsdcsg-pVRhWGR60Ty3wtmFuy4RA6Sr0/edit#slide=id.g44a5b3247e_0_69)

私は普段Juliaしか使わないのでこのcondaの話とは無縁だったのだが、研究室の仲間たちがこれでややこしいことに巻き込まれているのを救うためにも、Pythonの人たちのために記事を書いておく。

今まで私のMacの環境では、
brew -> pyenv -> conda
としていたところを、

brew -> direnv
公式サイト -> conda
として組み合わせて利用する。
以下、macOSでの手順を書くが、他のOSでも似たような手順でdirenvでcondaを分離しておくことはできるはずだ（必要性があるかどうかは知らないが）。

## direnvでcondaを利用する手順

### 1. brew

まずdirenvをインストールするためにHomebrewを入れる。MacPortsにもdirenvはあるようだ。brewを使いたくない人は別の方法でインストールすれば良い。

```bash
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```

### 2. direnv

brewでdirenvをインストールする。

```bash
brew install direnv
```

インストールされたかどうか確認する。

```bash
direnv
```

[direnvの公式サイトのsetupの項目](https://direnv.net/index.html#setup)にあるように、

bashなら

```bash
# ~/.bashrc
eval "$(direnv hook bash)"
```

fishなら

```fish
# ~/.config/fish/config.fish
eval (direnv hook fish)
```

とする。他のシェルはサイトを見ること。

### 3. conda (anaconda か miniconda)

Anacondaの公式サイトからAnacondaかMinicondaをインストールする。私はMinicondaを入れた体験に基づいてこの記事を書いている。Anacondaの人は適宜Minicondaに読み替えて欲しい。

以下の公式サイトの手順に従うが、

[Conda: Installation](https://conda.io/docs/user-guide/install/index.html)

インストール時の設定の際に、環境変数の設定するかと聞かれたらnoにしておく。自分でそこを管理するのが目的なので。インストールする場所は個人的に見えるのが嫌なので、デフォルトだと`~/minoconda`のものを`~/.miniconda`に置いている。

インストールが完了したら、

```bash
# インストール場所がデフォルトなら
~/miniconda/bin/python
# 私は場所を変えているので
~/.miniconda/bin/python
```

としてインストールされたバイナリがちゃんと起動するかどうか確認する。

```bash
↪ ~/.miniconda/bin/python
Python 3.7.0 (default, Jun 28 2018, 07:39:16)
[Clang 4.0.1 (tags/RELEASE_401/final)] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
```

確認したら`exit()`としてPythonを終了する。

### 4. direnvで動的にcondaのバイナリをpathに追加

このままだとminicondaのpythonを呼び出すたんびに、`~/miniconda/bin/python`と打つ必要があるので、`$HOME/miniconda/bin`を環境変数`PATH`に追加したいが、

```bash
# bashなら~/.barhrc
# miniconda
export PATH=$HOME/miniconda/bin:$PATH
```

```fish
# fishなら~/.config/fish/config.fish
# miniconda
set -x PATH $HOME/miniconda/bin $PATH
```

そのまま追加するとシステムのバイナリたち`/usr/local/bin/python`とかを覆って見えなくしてしまうので、direnvを使う。Minicondaを利用したい作業ディレクトリのルートに、`.envrc`を置き、そこに設定を書けば良い。

例えば、`~/work`以下のディレクトリでminicondaで覆ってしまう場合は、

```bash
# ~/work/.envrc
# miniconda
export PATH=$HOME/miniconda/bin:$PATH
```

とすれば良い。このとき、`.envrc`の設定はbashで書かなければならないことに注意する（fishなどの他のシェルを使っていても）。行った`.envrc`の変更を許可し、

```bash
direnv allow ~/work/.envrc
```

シェルを再起動して、`cd ~/work`とすれば、

```bash
paalon at mac in ~
↪ python
Python 2.7.14 (default, Mar 22 2018, 14:43:05)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> exit()
paalon at mac in ~
↪ cd work
direnv: loading .envrc
direnv: export ~PATH
paalon at mac in ~/work
↪ python
Python 3.7.0 (default, Jun 28 2018, 07:39:16)
[Clang 4.0.1 (tags/RELEASE_401/final)] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> exit()
paalon at mac in ~/work
↪
```

これによってpyenvの覆い効果と同等のことがdirenvでできる。

## まとめ

これによって(ana or mini)conda利用者はpyenvから開放されることになり上記のような疑問はdirenvによって解決されることになる。direnvを利用するメリットとしては、pyenvはその内蔵のcondaだけ使っている場合は特に不便を感じないのだが、他のものと組み合わせて利用しようとするときに、融通が効かなくて困ることが多かった。Anaconda特有の環境変数マスクを解決する場合にはpyenvよりもdirenvの方が目的にかなっておりスマートであると私は思っている。（PythonなんかよりJuliaの方が便利だということは置いておいたとして…）