Pipenvことはじめ

ずっと気になりつつ手を出せていなかった`Pipenv`をちゃんと試してみました。

## pipenvのインストール・セットアップ

まずはインストールから

```bash
# python3 環境で試してみたいため
pip3 install pipenv
```

インストール後、作業ディレクトリに移動

```bash
# 使用するバージョンを厳密に指定する
pipenv --python 3.7.2

# 上記以外にも下記のようにアバウトな指定も可能
# Python 3を使う場合
# pipenv --three  
# Python 2を使う場合
# pipenv --two  
```

これでcurrentに`Pipfile`ができる。
`cat Pipfile`で中身を見てみる

```bash
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]

[requires]
python_version = "3.7"
```

## pipenvを用いたライブラリのインストール

ためしに`beautifulsoup4`と`requests`を`pipenv`を使ってインストールしてみる。

```bash
$pipenv install beautifulsoup4 requests
Installing beautifulsoup4…
Adding beautifulsoup4 to Pipfile's [packages]…
✔ Installation Succeeded
Installing requests…
Adding requests to Pipfile's [packages]…
✔ Installation Succeeded
Pipfile.lock not found, creating…
Locking [dev-packages] dependencies…
Locking [packages] dependencies…
✔ Success!
Updated Pipfile.lock (b65c9c)!
Installing dependencies from Pipfile.lock (b65c9c)…
  🐍   ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 7/7 — 00:00:01
To activate this project's virtualenv, run pipenv shell.
Alternatively, run a command inside the virtualenv with pipenv run.
```

`cat Pipfile`で再度`Pipfile`の中身を見てみる。

```bash
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
beautifulsoup4 = "*"
requests = "*"

[requires]
python_version = "3.7"
```

`packages`の中に追記されているのが分かる。
ちなみにバージョンを指定しないでインストールすると`*`になるよう。
ただ、インストールしたバージョンについては`Pipfile.lock`なるものが作られて、こちらで管理されるよう。
`npm`でいう`package-lock.json`、
`yarn`でいう`yarn.lock`、
`bundler`でいう`Gemfile.lock`のようなものですね。

`package.json`でいう`devDependencies`の仕組みもあって、例えば開発時にのみ`flake8`を使いたいってときは`--dev`をつけて実行すれば良い。

```bash
$pipenv install --dev flake8
Installing flake8…
Adding flake8 to Pipfile's [dev-packages]…
✔ Installation Succeeded
Pipfile.lock (cbc640) out of date, updating to (b65c9c)…
Locking [dev-packages] dependencies…
✔ Success!
Locking [packages] dependencies…
✔ Success!
Updated Pipfile.lock (cbc640)!
Installing dependencies from Pipfile.lock (cbc640)…
  🐍   ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 12/12 — 0
```

`Pipfile`にも`dev-packages`として依存関係が追加されている。

```bash
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]
flake8 = "*"

[packages]
beautifulsoup4 = "*"
requests = "*"

[requires]
python_version = "3.7"
```

## pipenv環境で依存ライブラリのインストール

使う場面としては、`git`で`clone`してきたものが`pipenv`を使っている場合などに使用。
`Pipfile.lock`の依存関係を見て、必要なライブラリをインストールしてくれる。


```bash
$pipenv install
```

`npm`でいう`npm install`と同じもの。

## requirements.txt で管理しているプロジェクトにPipenvを導入する場合

例えば下記のように `requirements.txt` で管理されているプロジェクトがあったとする。

```bash
$cat requirements.txt
beautifulsoup4==4.8.1
requests==2.22.0
```

`pip install`の場合は下記のように実施して依存ライブラリをインストールすることになるが、

```bash
pip install -r requirements.txt
```

`pipenv`を導入する場合は、上に書いた通りのコマンド(下記を参照)を実施すれば、勝手に依存ライブラリを認識して`Pipfile`を作成してくれる。


```bash
pipenv --python 3.7.2
```


上のコマンドを叩いた際に、下記のようなログも一緒に流れて、Pipfileに変換してくれたことが分かる。

```bash
requirements.txt found, instead of Pipfile! Converting…
✔ Success!
Warning: Your Pipfile now contains pinned versions, if your requirements.txt did.
We recommend updating your Pipfile to specify the "*" version, instead.
```

実際に出力された `Pipfile` は下記の通り。

```bash
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
beautifulsoup4 = "==4.8.1"
requests = "==2.22.0"

[requires]
python_version = "3.7"
```

簡単に`pipenv`への管理に移行できるのはとても良い。

## pipenv環境での実行手順(shell)

ちなみにこの状態で`python3`を実行して下記のようにインストールしたpackageを読み込もうとするとエラーになる。

```bash
$python3
Python 3.7.2 (default, Feb 11 2019, 09:42:34)
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'requests'
```

`npm`とかだと、そのまま読み込めるところだけど、`pipenv`はそうは行かないよう。
上に貼り付けたログにも出ていたが、`pipenv shell`をすると、pipenvで作った環境内で`shell`を起動できるようだ。

```bash
# shell起動
$pipenv shell
Launching subshell in virtual environment…
 . /Users/username/.local/share/virtualenvs/dirname-85OFZKXi/bin/activate
~/dirname : $ . /Users/username/.local/share/virtualenvs/dirname-85OFZKXi/bin/activate

# 仮想環境がactivateされた状態となる
(dirname) ~/dirname : $python3
Python 3.7.2 (default, Feb 11 2019, 09:42:34)
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
>>> exit()

# 仮想環境から抜けるときはdeactivate
(dirname) ~/dirname : $deactivate

# 追記: 次回pipenv shell実行時にエラーが出るのでサブシェルも終了させる
~/dirname : $exit
```

サブシェルの終了について下記に追記しています。
[pipenv shellをしても実行できないとき](#pipenv-shellをしても実行できないとき)

## pipenv環境での実行手順(ファイル実行)

試しにこんなスクリプト(`sample.py`)を書いてみました。
無慈悲なコードですね。

```python
import requests
print(requests)
```

これも当然ながら`python3 sample.py`と実行するとエラーになります。

```bash
$python3 sample.py
Traceback (most recent call last):
  File "sample.py", line 1, in <module>
    import requests
ModuleNotFoundError: No module named 'requests'
```

`pipenv run python3 sample.py`で実行するようです。

```bash
$pipenv run python3 sample.py
<module 'requests' from '{省略}'>
```

## 独自のコマンドを定義する(scripts)

ちなみに`pipenv run sample.py`と実行してみたら下記のようなエラーが出てきた。

```bash
$pipenv run sample.py
Error: the command sample.py could not be found within PATH or Pipfile's [scripts].
```

https://pipenv-ja.readthedocs.io/ja/translate-ja/advanced.html#custom-script-shortcuts

どうやら独自のコマンド定義ができるよう。
(`package.json`内に書く`scripts`みたいなやつ)

Pipfileに下記を追記する

```pipfile
[scripts]
sample = "python3 sample.py"
```

後は実行すればOK

```bash
$pipenv run sample
<module 'requests' from '{省略}'>
```

## 独自のコマンドに引数を渡す(scripts)

ちなみに定義したコマンドには引数も渡せるよう。

テストのためのスクリプト

```python
import sys
args = sys.argv
print(args)
```


pipfileで下記のように定義

```pipfile
[scripts]
argvtest = "python3 argvtest.py"
```

引数を付けて実行してみると、ちゃんと引数を渡してくれている

```bash
$pipenv run argvtest AAAAAAA BBBBBBBBBB CCCCCCCCCCCCCCC
['argvtest.py', 'AAAAAAA', 'BBBBBBBBBB', 'CCCCCCCCCCCCCCC']
```


## requirements.txtに書き出す
`pipenv lock -r`を実行すると、依存関係にあるパッケージが`requirements.txt`形式で吐き出される。

```bash 
$pipenv lock -r
-i https://pypi.org/simple
beautifulsoup4==4.7.1
certifi==2018.11.29
chardet==3.0.4
idna==2.8
requests==2.21.0
soupsieve==1.7.3
urllib3==1.24.1
```

ちなみに`pipenv lock -r -d`を実行すると`--dev`でインストールしたパッケージのみが表示される。

```bash
$pipenv lock -r -d
-i https://pypi.org/simple
entrypoints==0.3
flake8==3.7.5
mccabe==0.6.1
pycodestyle==2.5.0
pyflakes==2.1.0`
```

例えば開発依存も含めた状態で`requirements.txt`をエクスポートする場合は

```bash
pipenv lock -r > requirements.txt
pipenv lock -r -d >> requirements.txt
```

と打てばよいのかと思ったけど、そもそも`pipenv`のやり方で環境構築すればいいわけだし、
使用用途してあるのは、開発依存は除外した状態で`requirements.txt`を作成するぐらいなのか
(ここらへん、実際どうなんでしょう？)

## セキュリティの脆弱性の検知

下記コマンドで既知のセキュリティの脆弱性が無いか依存関係グラフを走査してくれるよう。

https://pipenv-ja.readthedocs.io/ja/translate-ja/advanced.html#detection-of-security-vulnerabilities

```bash
$pipenv check
Checking PEP 508 requirements…
Passed!
Checking installed package safety…
All good!
```

## インストールしたパッケージをエディターで開く

コードリーディングしたい方にとっては便利かも
下記のコマンドを打つと、設定されているエディターでパッケージのディレクトリを開いてくれます。
(私の場合は`vim`が起動してディレクトリが開かれる感じ)

```bash
pipenv open requests
```

https://pipenv-ja.readthedocs.io/ja/translate-ja/advanced.html#open-a-module-in-your-editor

## 環境変数:TODO

疲れたので、これは後で調べようと思います。
ドキュメントはここに書かれているようですね。

https://pipenv-ja.readthedocs.io/ja/translate-ja/advanced.html#injecting-credentials-into-pipfiles-via-environment-variables

## pipenv shellをしても実行できないとき

`pipenv shell`をしても`Shell for UNKNOWN_VIRTUAL_ENVIRONMENT already activated.`というメッセージが出てきて実行できないとき

`pipenv shell`をすると下のようなエラーが出て、shellが実行できないときがあります。
(実際には`pipenv shell`を`deactivate`で終了させたあとに出てくるよう)

```bash
$pipenv shell
Shell for UNKNOWN_VIRTUAL_ENVIRONMENT already activated.
No action taken to avoid nested environments.
```

調べてみるとこのissueにたどり着きました。
[pipenv shell, then deactivate should display warning #736](https://github.com/pypa/pipenv/issues/736)

どうやら`pipenv shell`は仮想環境でサブシェルを起動しているので、一度`exit`でサブシェル自体を終了させないとこのようなエラーが出てしまうということらしい。

たしかに`pipenv shell`って打つと下のようなメッセージが出てきていましたね。

```
Launching subshell in virtual environment…
```

個人的に混乱ポイントでしたので、備忘録がてら追記しました。

`pipenv shell`で仮想環境を立ち上げたときは、最後に`exit`でサブシェル自体も終了させるのをお忘れなきよう。

## 感想
`npm`みたいに管理できるようになったのは嬉しいです。
特に今まではテストやフォーマット関連のライブラリも一緒くたに入れることになっていたので、それによる気持ち悪さが`dev-packages`で解消できていたり、独自のコマンドも定義できるようになったので、開発もよりやりやすくなりそう。
ひとまず今後は`Pipenv`を基本使うようにしてみようと思います。
(どちらかというとPython触る機会自体は少ない自分ですが...😂)

## 参照資料

公式ドキュメント(日本語版)
https://pipenv-ja.readthedocs.io/ja/translate-ja/index.html
https://pipenv-ja.readthedocs.io/ja/translate-ja/advanced.html


github
https://github.com/pypa/pipenv