Pipenvを使ったPython開発まとめ

# はじめに

今年は複数人でいっしょにPythonを使って開発を行う機会があったのですが，そのときに導入したPipenvがいい感じでしたので一通りの基本操作についてまとめてみようと思います．個人での開発でも有効だと思いますが，複数人で開発するときに便利かなと感じたところも多かったので，そういう視点も入れながらまとめてみました．
※個人的にチームでの開発でよかった内容に:christmas_tree:をつけています．

# Pipenvとは

Pythonで開発するときに，プロジェクト毎のパッケージ管理や仮想環境の構築を簡単に自動で行ってくれるツールです．Node.jsのnpmなんかを使ったことがあればイメージがわきやすいと思います．
パッケージのインストールならpip，仮想環境の構築ならvirtualenv（venv）を使えば対応できますが，pipenvはそれらをまとめてより簡単に扱えるようサポートしてくれます．またnpm-scriptsのようにコマンドに名前をつけて登録することもできます．

# Pipenvのインストール

Pipenv自体のインストールはpipから行えます．簡単ですね．

```console
$ pip install pipenv
```

# Pipenvの使い方

Pipenvの基本的な使い方を順にまとめていきます．この記事を書いたときの手元の環境は次の通りです．

* Windows 10 Pro，Debian 9
* Python 3.7.1
* pipenv 2018.11.14

## 初期化

まずは新規のプロジェクトの初期化方法です．
プロジェクトのディレクトリに移動して次のコマンドを実行すると，自動で仮想環境が作成されて`Pipfile`というファイルが生成されます．Pythonのバージョンの指定は`3.7`など，より詳細に指定もできます．

```console
$ pipenv --python 3    # Python3系で初期化する例です
```

またpyenvを使用している場合は，環境に入っていないバージョンを指定したときにはpyenvと連動してPythonのインストールが自動的に行われます．

```console
$ pipenv --python 3.6
Warning: Python 3.6 was not found on your system…
Would you like us to install CPython 3.6.7 with pyenv? [Y/n]: y
Installing CPython 3.6.7 with pyenv (this may take a few minutes)…
⠦ Installing python...
```

## パッケージのインストール

パッケージのインストールは次のように行います．このときも（まだ作っていなければ）仮想環境が自動的に作られて，そこにパッケージがインストールされます．またpipenvからパッケージをインストールするとPipfileにパッケージが追加されます．

```console
$ pipenv install numpy    # numpyをインストールする例です
```

```toml:Pipfile
[packages]
numpy = "*"
```

またこのときに`Pipfile.lock`が自動で生成され，実際にインストールされたパッケージの詳細なバージョンや依存パッケージの情報などが記録されます．これをもとに他PCで環境を再現することが簡単にできます．

:christmas_tree:pipenvを使うと，このようにパッケージの管理や仮想環境の生成が自動的に行われて便利です．チーム内でPythonに不慣れなメンバーがいても，pipを直接使って`requirements.txt`を更新し忘れてしまったり，仮想環境を作らずに関係のないパッケージまで管理されてしまったりを防ぐことができます．

### 開発用パッケージのインストール

通常のパッケージのインストールの他に`--dev`オプションを使うことで，開発環境のみで使うパッケージを別枠（dev-packages）で管理しながらインストールすることができます．これを利用して開発プロジェクト内でコードの整形や静的解析の利用を促すこともできます．

```console
$ pipenv install --dev autopep8 flake8
```

```toml:Pipfile
[dev-packages]
autopep8 = "*"
flake8 = "*"
```

:christmas_tree:このように開発用のパッケージを切り分けて管理できると，コードの品質向上につなげることができるパッケージをチームに布教しやすいです．（後述するScriptsも活用するとより良いです．）

### requirements.txtからのインストール

Pipenvの導入前に`requirements.txt`でパッケージが管理されていた場合に，その内容からPipenvでインストールすることもできます．

```console
$ pipenv install -r ./requirements.txt
```

## Pipfile，Pipfile.lockから環境の再現

すでに誰かが環境を作成してPipfileがgitなどで管理されていると，他のメンバーも簡単に環境を作成することができます．
次の方法だと，Pipfileの中で管理されているパッケージがインストールされます．このときにインストールされた内容でPipfile.lockが更新されます．

```console
$ pipenv install
$ pipenv install --dev    # --devオプションで通常のパッケージの他に開発用パッケージもインストールされます
```

同様にPipfile.lockが手元にあり，PipfileでなくPipfile.lockから詳細なバージョンなども合わせて環境を作成したい場合は次のようにします．
この場合は，PipfileでなくPipfile.lockを使ってインストールされ，環境を再現することができます．

```console
$ pipenv sync
$ pipenv sync --dev    # 開発用パッケージもインストールしたい場合はinstallと同様に--devオプションを
```

:christmas_tree:上記のように既に開発中のプロジェクトに新しいメンバーが追加になっても，開発環境をコマンド一つで準備することができます．

## :christmas_tree:スクリプトの登録

独自のスクリプトを名前を付けて登録することができます．npmなんかでもよく使われていると思いますが，これが個人的にはチーム開発するときにとても便利だと思います．
プロダクトのmainスクリプトのキックやUnitテストの実行，コードの整形やLintツールの実行などを登録しておけば，詳しいドキュメントをチームで共有する手間も省けますし，実際にメンバーに使ってもらいやすいという印象です．

使い方は，Pipfileに`[scripts]`を用意して登録します．例えば次のような感じです．

```toml:Pipfile
[scripts]
start = "python main.py runserver"
test = "python -m unittest discover -v"
format = "autopep8 -ivr ."
lint = "flake8 --show-source ."
```

スクリプトの実行は次のように行います．

```console
$ pipenv run start    # [scripts]のstartを実行する例です
```

また，scriptsに登録するほどでもないPythonのコードを個別に実行するときは，次のように呼び出すことができます．

```console
$ pipenv run python spam.py    # spam.pyを仮想環境に入らずに実行する例です
```

## 仮想環境関連の操作

pipenvで作られた仮想環境へ入るには次のように行います．よく使う操作はscriptsに登録しておきたいですが，ちょっとした操作を行うときには知っておくと便利です．

```console
$ pipenv shell
Launching subshell in virtual environment…
(mypipenv-XXXXXXXX) $ exit    # 抜けるときはexit
```

また仮想環境のパスが知りたい場合は次のように行います．

```console
$ pipenv --venv
C:\Users\username\.virtualenvs\mypipenv-XXXXXXXX
```

デフォルトではWindowsは`%userprofile%\.virtualenvs`，Linuxは`$HOME/.local/share/virtualenvs`以下に仮想環境が生成されます．仮想環境を生成する場所を指定したい場合には，以下のように環境変数を設定します．

```console
$ export WORKON_HOME=~/venvs    # Windowsは`set WORKON_HOME=~/venvs`
$ pipenv --python 3             # ~/venvsディレクトリに仮想環境が生成される
```

またプロジェクトのディレクトリに仮想環境を作成したい場合は以下のように環境変数を設定します．

```console
$ export PIPENV_VENV_IN_PROJECT=true    # Windowsは`set PIPENV_VENV_IN_PROJECT=true`
$ pipenv --python 3                     # ./.venvディレクトリに仮想環境が生成される
```

仮想環境を削除するには以下のように行います．

```console
$ pipenv --rm
```

## ちょっと高度なパッケージの管理関連

バージョンを指定してパッケージをインストールする場合は次のように行います．

```console
$ pipenv install numpy==1.14
```

また次のように書くこともできます．（推奨）

> The use of ~= is preferred over the == identifier as the latter prevents pipenv from updating the packages:

```console
$ pipenv install numpy~=1.14    # パッケージのメジャーバージョンをロックします（==1.*を使うのと同等）
```

GitHubのリポジトリを指定してインストールする場合は次のように行います．

```console
$ pipenv install git+https://github.com/<ユーザ>/<リポジトリ>.git@<リビジョン>#egg=<パッケージ名>
```

インストールされているパッケージのバージョンアップをするには次のように行います．

```console
$ pipenv update
```

インストールされているパッケージの一覧を確認するには次のように行います．依存関連が分かるように出力されて便利です．

```console
$ pipenv graph
autopep8==1.4.3
  - pycodestyle [required: >=2.4.0, installed: 2.4.0]
flake8==3.6.0
  - mccabe [required: >=0.6.0,<0.7.0, installed: 0.6.1]
  - pycodestyle [required: >=2.4.0,<2.5.0, installed: 2.4.0]
  - pyflakes [required: >=2.0.0,<2.1.0, installed: 2.0.0]
  - setuptools [required: >=30, installed: 40.6.2]
numpy==1.15.4
```

## .envの自動読み込み

プロジェクトに`.env`ファイルを用意しておくと，`pipenv run`や`pipenv shell`を実行するときに自動で読み込んでくれます．ハードコーディングするにはまずい内容やローカルのテスト環境ならではの設定を登録しておくと便利です．

```text:.env
DEBUG=1
```

```console
$ pipenv run python
>>> import os
>>> os.environ['DEBUG']
'1'
```

# :christmas_tree:おまけ

pipenvでinstallすると次のように蛇の絵文字が表示されます．（絵文字が表示できる環境で見ましょう．）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/45310/f74b3fc7-d940-336e-836e-faa52b3c3f41.png)

ハロウィンの日にはなんとパンプキンの絵文字が表示されていました！チームで開発しているとより一層もりあがりますね:jack_o_lantern:
![image.png](https://qiita-image-store.s3.amazonaws.com/0/45310/2198ec47-eb87-8693-e037-53137de8b4e2.png)

少しフライングですがクリスマスにはサンタさんが．．．ぜひクリスマスにPipenvを使ってコードを書きましょう:santa:
![image.png](https://qiita-image-store.s3.amazonaws.com/0/45310/b2f5b6a6-1bc7-b31a-3706-dc883b8c0778.png)