【Python】さようなら仮想環境？

Mediumで以下のような記事があった

[Goodbye Virtual Environments? – Chad Smith – Medium](https://medium.com/@grassfedcode/goodbye-virtual-environments-b9f8115bc2b6)

**さようなら、仮想環境？**

記事を見る感じ、python と pip の代わりになるようなコマンドっぽい

また、[PEP582](https://www.python.org/dev/peps/pep-0582/)で提案されている`__pypackages__`が使われている

---

ソースコードを見たり、READMEを見たり、Medinumを見たりしただけなので、間違いがあるかもしれません。
間違いがありましたら、教えていただけると嬉しいです。

## 動作環境

```
Python 3.7.1
```

## pythonloc

[cs01/pythonloc](https://github.com/cs01/pythonloc)

pythonとpipを使って実装されている小さなプロジェクト

仮想環境がいらなくなりそうな予感！？

## pythonlocのコマンド

以下のコマンドと対応している

| 通常                          | pythonlocを使うと...                       |
|-------------------------------|---------------------------------|
| $ python                        | $ pythonloc                       |
| $ pip                           | $ piploc                          |
| $ pip freeze > requirements.txt | $ pipfreezeloc > requirements.txt |

### piplocコマンド

`pip`の代わり

```
$ piploc install {pkg}
```

`./__pypackages__/{version}/lib`内に`pkg`がインストールされる

pipでのインストール先が`./__pypackages__/{version}/lib`となる

### pythonlocコマンド

`python`の代わり

```
$ pythonloc
```

PYTHONPATHに、`.:__pypackages__/{version}/lib:$PYTHONPATH`を設定された状態で、`python`([sys.executable](https://docs.python.org/ja/3/library/sys.html?highlight=sys%20executable#sys.executable))が実行される

### pipfreezelocコマンド

`pip freeze`の代わり

```
$ pipfreezeloc
```

`。・__pypackages__/{version}/lib`内のパッケージの情報を出力する

## pythonlocの何がすごいと思ったのか

`pipenv shell`みたいに仮想環境の有効化をしなくてもよくなる

これは、モジュールの検索順序の仕組みを利用してうまく実装されている

まず、Pythonはモジュールのインポートをするときに、インポートするモジュールを[sys.path](https://docs.python.org/ja/3/library/sys.html#sys.path)から検索する。

この`sys.path`についての説明は、[ドキュメント](https://docs.python.org/ja/3/tutorial/modules.html#the-module-search-path)に書いてあった

> spam という名前のモジュールをインポートするとき、インタープリターはまずその名前のビルトインモジュールを探します。見つからなかった場合は、 spam.py という名前のファイルを sys.path にあるディレクトリのリストから探します。 sys.path は以下の場所に初期化されます:

> ・入力されたスクリプトのあるディレクトリ (あるいはファイルが指定されなかったときはカレントディレクトリ)。
> ・PYTHONPATH (ディレクトリ名のリスト。シェル変数の PATH と同じ構文)。
> ・インストールごとのデフォルト。

pythonlocでは、`PYTHONPATH`に`.:__pypackages__/{version}/lib:$PYTHONPATH`を設定することによって、優先的に検索しに行くようにしている

### PYTHONPATHに`.:__pypackages__/{version}/lib:$PYTHONPATH`を設定している

PYTHONPATHについては、[ドキュメント](https://docs.python.org/ja/3/using/cmdline.html#envvar-PYTHONPATH)を参照 

まず、通常のpythonの場合

```python
$ python
>>> import sys, os, pprint

>>> os.environ.get('PYTHONPATH', '')
''

>>> pprint.pprint(sys.path)
['',
 'temp/test/pythonloc',
 '/Users/tamago324/.pyenv/versions/3.7.1/lib/python37.zip',
 '/Users/tamago324/.pyenv/versions/3.7.1/lib/python3.7',
 '/Users/tamago324/.pyenv/versions/3.7.1/lib/python3.7/lib-dynload',
 '/Users/tamago324/.local/lib/python3.7/site-packages',
 '/Users/tamago324/.pyenv/versions/3.7.1/lib/python3.7/site-packages']
```

次に、pythonlocの場合

```python

$ pythonloc
>>> import sys, os, pprint

>>> os.environ.get('PYTHONPATH', '')
'.:__pypackages__/3.7/lib:'

>>> pprint.pprint(sys.path)
['',
 '/temp/test/pythonloc',
 # ここに追加されている
 '/Users/tamago324/src/python/test/pythonloc/__pypackages__/3.7/lib',
 '/Users/tamago324/.pyenv/versions/3.7.1/lib/python37.zip',
 '/Users/tamago324/.pyenv/versions/3.7.1/lib/python3.7',
 '/Users/tamago324/.pyenv/versions/3.7.1/lib/python3.7/lib-dynload',
 '/Users/tamago324/.local/lib/python3.7/site-packages',
 '/Users/tamago324/.pyenv/versions/3.7.1/lib/python3.7/site-packages']
```

このように、`__pypackages__/{version}/lib`は、`/Users/tamago324/.pyenv/versions/3.7.1/lib/python3.7/site-packages`よりも前にあるため、`__pypackages__/{version}/lib`が優先的に検索されるようになる

## まとめ

[pythonloc](https://github.com/cs01/pythonloc)の考え方すごい。`__pypackages__`すごい。[PEP582](https://www.python.org/dev/peps/pep-0582/)すごい。

## 参考文献

- [Goodbye Virtual Environments? – Chad Smith – Medium](https://medium.com/@grassfedcode/goodbye-virtual-environments-b9f8115bc2b6)
- [6. モジュール (module) — Python 3.7.2 ドキュメント](https://docs.python.org/ja/3/tutorial/modules.html#the-module-search-path)
- [【Python】sys.pathに追加したディレクトリからimportする処理でPEP8に違反した際の対処 - Qiita](https://qiita.com/ninomiyt/items/76f6f7e3c1f833cf99e5)
- [PEP 582 -- Python local packages directory | Python.org](https://www.python.org/dev/peps/pep-0582/)