はじめに
最近話題のpipenvですが、実際にどのような開発フローをするのかわかりづらいところがあります。この記事ではGitとDockerの利用を前提としつつ、pipenvを使うための開発フローを解説します。
全体の流れ
- 開発環境のインストール
- プロジェクトの初期化
- Docker化
前提環境
- Python3系
- UnixライクなOS
開発環境のインストール
まずは開発環境のインストールです。pipenvを入れていきましょう。
pipenvのインストール
macOSの場合
Homebrewで簡単に導入可能です。
$ brew install pipenv
Arch Linuxの場合
こちらもリポジトリに登録されているので簡単に導入できます
$ sudo pacman -S python-pipenv
Ubuntu 20.04の場合
公式リポジトリからインストールできるようになりました!
$ sudo apt install pipenv
インストール後の設定
pipenvをインストールしたら、PIPENV_VENV_IN_PROJECT
環境変数をセットしておきます。pipenvで作成される仮想環境はデフォルトで~/.local/share/virtualenvs
に格納されますが、この設定を有効にするとプロジェクトルートディレクトリの.venv
ディレクトリに仮想環境が作成される挙動に変更されます。特にPyCharmで開発するときはこの.venv
ディレクトリをデフォルトで読み込んでくれるため、この設定を有効にしておくことをオススメします。
お使いのシェルの設定ファイル(~/.bashrc
や~/.zshrc
など)を開き、以下の行を追加してください。追加したら端末を再起動するかsource
コマンドを使って設定を再読込してください。
export PIPENV_VENV_IN_PROJECT=1
その他ツールの導入
これから利用するGit
とDocker
を導入しておいてください。ここでは本題から外れるため、詳細なインストール手順は省きます。
Pythonプロジェクトの初期化
ここからはいよいよPythonプロジェクトを作成していきます。
プロジェクトの作成
適当なディレクトリを作り、pipenv install
コマンドを使ってプロジェクトを初期化します。このとき--python
オプションを使うことでpyenvを使って自動的にそのバージョンをインストールしてそこから仮想環境を切り出してくれます。
$ mkdir sample_project
$ cd sample_project
$ pipenv install
Creating a virtualenv for this project…
一連の作業が終わってからls
すると、Pipfile
とPipfile.lock
の2種類のファイルができあがっていることが確認できます。これで初期化は完了です。
$ ls
Pipfile Pipfile.lock
Gitの初期化
続いてこのプロジェクトをGitで管理するためにGitの初期化を行います。gitignore.ioを利用して、まず.gitignore
ファイルを作ってしまいましょう。今回はPythonとvirtualenvを指定して作ってもらいます。.gitignore
に追加の設定が必要な方はここで編集しておいてください。
.gitignore
ができたら手元のGitリポジトリを初期化して、ざっくり全ファイルを追加してcommitしておきます。PipfileとPipfile.lockはともにGit管理下に置くことを想定しているファイルですので、忘れずにaddしておきましょう。
$ curl -o .gitignore https://www.gitignore.io/api/python,virtualenv
$ git init
$ git add Pipfile Pipfile.lock .gitignore
$ git commit -m "Initial Commit"
パッケージの追加
pipenv環境下ではpipenv
コマンドを使ってパッケージをインストールします。こうすることでPipfile
にそのパッケージが列挙され、その時点で解決されたバージョンやそのパッケージの依存関係にある別のパッケージの詳細がPipfile.lock
に記載されます。今回は試しにDjangoをインストールしてみることにします。
$ pipenv install django
インストール後にそれぞれのファイルの中身を覗いてみましょう。
Pipfileにはdjango = "*"
が追加されています。これは、インストール時点での最新版を使う設定です。もしバージョンを固定したい場合はインストール時にpipenv install django==2.0.0
などとすると良いでしょう。django>=1.11.0,<2.0.0
のように範囲指定をすることもできます。
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
django = "*"
[dev-packages]
[requires]
python_version = "3.7"
Pipfile.lockには依存関係にあるパッケージも列挙されています。また、パッケージのハッシュ値も記載されるようになっています。このハッシュ値はダウンロードされたパッケージそのものから生成されており、これによって異なる環境下でも同一のパッケージがインストールされることを保証することができます。
{
"_meta": {
"hash": {
"sha256": "627ef89f247ecee27e9ef0dabe116108d09c47abf171c900a8817befa64f9dd2"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"django": {
"hashes": [
"sha256:97886b8a13bbc33bfeba2ff133035d3eca014e2309dff2b6da0bdfc0b8656613",
"sha256:e900b73beee8977c7b887d90c6c57d68af10066b9dac898e1eaf0f82313de334"
],
"index": "pypi",
"version": "==2.0.7"
},
"pytz": {
"hashes": [
"sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053",
"sha256:ffb9ef1de172603304d9d2819af6f5ece76f2e85ec10692a524dd876e72bf277"
],
"version": "==2018.5"
}
},
"develop": {}
}
もし追加のパッケージをインストールしたくなったら、その時点でいつでもpipenv install
コマンドを使ってパッケージを追加してください。
また、開発時にしか使わないパッケージを導入することも可能です。例えばLinterにblack
とmypy
を採用することにした場合、以下のように-d
オプションを付けてインストールします。なおblackはstable版が存在していない都合上、バージョンを明示的に指定しないとバージョン解決に失敗してインストールできないようです。
$ pipenv install -d black==19.10b0 mypy
もろもろをインストールしたら、Pipfile
とPipfile.lock
をaddしてcommitしておきましょう。
$ git add Pipfile Pipfile.lock
$ git commit -m "install django, black and mypy"
Pythonスクリプトの実行
pipenvで作られるのは仮想環境であるため、pipenvでインストールしたパッケージを利用したPythonスクリプトを実行するためにはまずその仮想環境に入る必要があります。以下のコマンドで仮想環境に入れます。仮想環境に入るとプロンプトの先頭に仮想環境名が表示されるようになります。仮想環境を抜けるときはexit
コマンドを利用します。
$ pipenv shell
(sample_project)$ python foo.py
(sample_project)$ exit
Docker化
ここからはDockerに載せるための設定をしていきます。
.dockerignoreの準備
まず余計なファイルを転送しないために.dockerignore
を書きましょう。特に.venv
ディレクトリは無視するようにします。その他不要なものも一緒に書いておくと良いでしょう。
.mypy_cache/
.venv/
__pycache__/
Dockerfileの書き方
.dockerignore
が書けたらDockerfile
を書きます。公式のPythonイメージから作っていきます。プログラムを多少変えただけなのにいちいち依存パッケージのインストールから始めるのは無駄なので、まずはPipfile
とPipfile.lock
だけを転送し、先にパッケージをインストールしてから、Pythonスクリプトをコピーして作成したアプリケーションを実行するようにします。
また、Python公式イメージにはデフォルトでpipenvが含まれていないため、Dockerfile内でインストールする必要があります。pipenvがインストールできたら、依存パッケージをインストールします。このとき、開発用のパッケージは導入する必要が無いので-d
オプションは付けないでおきます。
また、--system
オプションを使うことで、システムデフォルトのpip
を使ってパッケージをインストールしてくれます。Dockerで使う場合はこのオプションを付けると良いでしょう。逆にそうしないといちいちpipenvで作成した仮想環境に入る手間が加わってしまいます。
今回はマルチステージビルドを利用して、pipenvでインストールしたPythonパッケージを動作用イメージにまるっとコピーして、出来上がりサイズを小さくできるようにしています1。
FROM python:3.8 AS builder
WORKDIR /usr/src/app
COPY Pipfile Pipfile.lock ./
RUN pip install pipenv \
&& pipenv install --system
FROM python:3.8-slim
ENV PYTHONUNBUFFERED=1
COPY --from=builder /usr/local/lib/python3.8/site-packages /usr/local/lib/python3.8/site-packages
COPY . ./
CMD ["python", "app.py"]
あとはよしなに開発を進めてください。
FAQ
パッケージの更新方法
pipenvで管理しているパッケージに更新があった場合は、pipenv update
コマンドでそれらのパッケージを依存関係ごとアップデートすることができます。
パッケージの削除方法
不要になったパッケージはpipenv uninstall PACKAGE_NAME
で削除できます。
途中から開発に参加した場合のパッケージインストール方法
途中から開発に参加し、まっさらな環境の人は環境構築とGitリポジトリのcloneを済ませた上で、Pipfile
が存在しているディレクトリで以下のコマンドを入力すれば開発に必要なパッケージを全て導入することが可能です。
$ pipenv install -d
別のブランチでインストールされたパッケージをマージ後に適用する方法
共同開発でブランチを多数切っていると、他のブランチでもパッケージが追加されたものがmasterにマージされ、それを手元のブランチにrebaseやマージするなどして取り込んだときにPipfile.lock
がコンフリクトする現象が発生します。なおPipfile
の方はよしなにマージされることが多いです。よしなにマージされなくてもコンフリクト箇所を注意深く観察して手動解決できます。
Pipfile.lock
がコンフリクトしている場合は、コンフリクトが発生している状態でいったん手元のPipfile.lockを削除し、新しく生成し直す方法を使います。コンフリクトが解決したら忘れずにadd
しておきましょう。以下にコマンド例を示します。
$ rm -rf Pipfile.lock # お好みで`git rm`でも可
$ pipenv install
$ git add Pipfile.lock
GitリポジトリからPythonパッケージをインストールしたい場合
開発版など、Gitリポジトリにしかパッケージが存在していない場合があります。その場合も、pipenvを使ってパッケージをインストールすることが可能です。この場合少し表記が複雑になりますが
- リポジトリのアドレス
- コミットハッシュやタグ名などのリビジョン
- パッケージ名
の3つを抑えておけばOkです。あとは下記のフォーマットに従って入力すれば大丈夫です。
$ pipenv install git+[git_repository_url]@[revision]#egg=[package_name]
例えば拙作のhackingのフォークをGitリポジトリから導入する場合、以下のようにします。今回は開発用にしか使わないので-d
オプションを付けています。リビジョンはmasterブランチの最新のコミットから取得したものです。パッケージ名は自由に付けて良いのですが、わかりやすくhacking
にしてあります。
$ pipenv install -d git+https://github.com/aruneko/hacking@733ffe4a711ed6c92063d8074410ac05c97608dc#egg=hacking
おわりに
ここまでpipenvとGitとDockerを使ったPython開発フローを紹介してきました。virtualenvなどを使っていた頃と比べるとかなりシンプルに開発できていると思います。また、Pipfile.lockのおかげで再現性のある環境をどこでも立ち上げることができるようになり、利便性も増しています。皆さんもぜひpipenvを使ってPython開発環境を快適にしてみましょう!
-
参考: フューチャー開発者ブログ ↩