Pipenvについて

2019年5月23日木曜日

pythonパッケージングツールの決定版と言われているpipenvについて紹介します。

このツールの目的は複数のpython本体やpythonパッケージ群のバージョンを分けて管理し、必要に応じた切り替えを可能とすることです。
また、パッケージ環境の再現が可能ですので、github等でコードを公開する際には大変便利です。


pipenv以前にはvirtualenv(venv)やそのラッパーツールが使われていました。
python本体のみのバージョン管理/切り替え用にはpyenvというツールもありますが、python本体とパッケージ群のバージョンは一括で管理したほうがよいため、最近はpyenvは不要とされることも多いです。
pyenvではバージョン切り替えは行わず、未インストールのpythonバージョンをダウンロード、インストールするツールとしてのみの用途では相変わらず有用ではあるようです。
pipenvにはpyenvと連携してpython本体バージョンをインストールする機能もあります。 


最初にあげた目的は概ねvirtualenvでも実現可能です。
実現可能なのですが、Node(Javascript)のnpmやRubyのbundler等の同様の目的を持つツールと比べてvirtualenvはかなりイケてなかったために、virtualenvを使わずにpythonパッケージのバージョン差異の問題に悩まされるユーザーが後を絶ちません。
virtualenvの何がイケてないのか。
最大の欠点はUIでしょう。

 恥を偲んで言ってしまうと、かくいう僕もpipenv登場まではvirtaulenvをインストールはしていたものの、使っていませんでした。

なんかわかりにくいし、面倒そうだと思ったからです。
その点、npmは後にいろいろハマることにはなりますが、とりあえずスムーズに使い出すことができました。


virtualenvのコマンドは言わば手続き的、比べてnpmは宣言的と言えるでしょう。
要するにnpmの方が同じことをするのにも手順が少ないのです。
操作していることも(初学者にとってある意味)、わかりやすいのです。

npmでは下記のようなコマンドになります。

$ mkdir my_project
$ cd my_project
$ npm init
$ npm install hogehoge 

わかります。
初期化して、hogehogeをインストールしているのです。

virtualenvでは下記のようなコマンドになります。
$ mkdir my_project
$ cd my_project
$ virtualenv my_env
$ source my_env/bin/activate
(my_env) $ pip install hogehoge

まず、npmではプロジェクト名のみでよかったのにvirtualenvでは仮想環境の名前(my_env)が必要となります。
この時点でダルい。
実用上、仮想環境はプロジェクトと紐づけて管理するので別途、名前を付ける必要はありません。
実際、pipenvでは勝手に付けてくれ、ユーザーが意識することはなくなります。
そして次に、source?、activate?
さらには、(my_env)?? 私のシェル、どうなっちゃったの?となるわけです。
まあ、ひとつひとつ理解していけば、npmでは内部的に行われていることがvirtualenvの方が理解しやすいということは言えるかもしれません。
理解する必要がないとは言いません。
ただ、結局pipenvがnpmをパクったことを考えれば、どちらがよかったかは明らかでしょう。


前置きが長くなりました。
pipenvを使ってみましょう。
pipenvのインストールについてはここでは説明しません。

$ mkdir my_project
$ cd my_project
$ pipenv install pandas 
pythonコードを書きます。 

(a.py)
import pandas as pd
df = pd.DataFrame([1,2,3], columns=['col1'])
print(df)
print('Hello Pandas!') 
書いたコードを実行してみます。

$ pipenv run python a.py
   col1
0     1
1     2
2     3
Hello Pandas!
 

簡単です。

みなさんはJupyter Notebookを使いたいのですよね。
$ pipenv install jupyter
$ pipenv run jupyter notebook 
OKです。

まあ、けっこう待たされますが。

僕はJupyterは使わず、もっぱらIPythonを使ってます。
jupyterをインストールするとIPythonも入るので、 
$ pipenv run ipython

問題ありません。

仮想環境にインストールしたパッケージを使うにはpipenv runでjupyter(ipython)を起動する必要があることに注意です。


ディレクトリに作成されたファイルを見てみましょう。
PipfileとPipfile.lockが作成されているはずです。
Pipfileの中身をみてみましょう。

 :
[packages]
pandas = "*"
jupyter = "*"
 :
[requires]
python_version = "3.6" 

インストールしたパッケージと使用しているpythonバージョンが記載されていますね。

Pipfile.lockを見てみましょう。
ごちゃごちゃと書いてありますね。
内容は上で明示的にインストールしたパッケージの他、その依存パッケージとインストールした具体的なバージョン等の情報が書かれています。
パッケージは随時更新されるので、pipenv install hogehogeとした場合にインストールされるパッケージのバージョンは時間が経つと変わってきます。
バージョン違いにより同じコードでも動かなくなったり、実行結果が変わったりします。
そこで、作成時のバージョンを記録しておいて、再現できるようにPipfile.lockがあるわけです。

virtualenvでもpip freeze > requirements.txtでパッケージバージョンの記録、pip install -r requirements.txtによりバージョンの再現が可能です。
しかし、Pipfileに当たるものがvirtualenvの利用上はありません。
Pipfileがあることにより、依存パッケージを除いたユーザーが「使いたいパッケージ」が明確になり、パッケージバージョンの最新化もし易くなります。
Pipfileに当たるものがない点がUIに次ぐvirtualenvの欠点となるでしょう。

pipenvの推奨する設定も紹介しておきます。
.zshrcや.bashrcに下記を記述します。

export PIPENV_VENV_IN_PROJECT=1 

これにより、仮想環境が.venvとして、プロジェクトのディレクトリ(カレントディレクトリ)内に作成されます。
デフォルトだと仮想環境は別のところにまとめて配置されており、プロジェクトのパスと仮想環境を紐づけて管理しているため、プロジェクトを移動するなどしてパスが変わると仮想環境とのリンクが切れてしまいます。

さて、これで他言語のパッケージ管理に追いついたと言われるpipenvですが、まだ大きな弱点を抱えています。
Pipfile.lockの作成にかかる時間が異常に長いのです(令和1年5月時点)。


続く前に、ところでAnacondaはどうなのか、という話ですが、シェル起動時に勝手に仮想環境に入ってしまうのが僕はだめでしたが、いつも使うパッケージをいちいちインストールする時間が節約できるのは大きいです。
特にCUIシェルまわりの弱いWindowsでは十分に選択肢に入るのかなという気はします。
ただ、githubにコードをあげるような場合はpipenvを使ったほうがよいでしょうね。