pipenvとGitとDockerを使ったPython開発フロー

More than 1 year has passed since last update.


はじめに

最近話題のpipenvですが、実際にどのような開発フローをするのかわかりづらいところがあります。この記事ではGitとDockerの利用を前提としつつ、pipenvを使うための開発フローを解説します。


全体の流れ


  1. 開発環境のインストール

  2. プロジェクトの初期化

  3. Docker化


前提環境


  • Python3系

  • UnixライクなOS


開発環境のインストール

まずは開発環境のインストールです。pyenvとpipenvを組み合わせて使います。


pyenvのインストール

pyenv単体で導入しても良いですが、rbenvやndenvなど後から他のenv系を使うことになると管理が面倒になるのでanyenvを利用します。

まずは本体をGit経由で引っ張ってきます。

$ git clone https://github.com/riywo/anyenv ~/.anyenv

お使いのシェルの設定ファイル(~/.bashrc~/.zshrcなど)を開き、以下の2行を追加します。追加したら端末を再起動してください。


~/.bashrc

export PATH="$HOME/.anyenv/bin:$PATH

eval "
$(anyenv init -)"

端末を再起動したらpyenvを導入します。これでpyenvの導入までは完了です。pyenvが導入できたら端末を再起動してください。

$ anyenv install pyenv

なお、anyenv-updateを導入しておくとpyenvの更新がコマンド一発でできるようになるので大変便利です。以下のコマンドで導入できます。

$ mkdir -p $(anyenv root)/plugins

$ git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update

あとは気が向いたときに以下のコマンドを実施すれば、anyenv管理下にあるenv系のシステムを一括で更新できます。

$ anyenv update


pipenvのインストール


macOSの場合

Homebrewで簡単に導入可能です。

$ brew install pipenv


Arch Linuxの場合

こちらもリポジトリに登録されているので簡単に導入できます

$ sudo pacman -S python-pipenv


Ubuntu 18.04の場合

残念ながらリポジトリには登録されていないので、pipのユーザーインストール機能を利用します。まずPython3をインストールし、pip3コマンドを使ってpipenvをインストールします。

$ sudo apt install python3 python3-pip

$ pip3 install --user pipenv

インストールが完了したら、~/.local/binにPATHを通します。お使いのシェルの設定ファイル(~/.bashrc~/.zshrcなど)を開き、以下の行を追加してください。


~/.bashrc

export PATH="$HOME/.local/bin:$PATH



インストール後の設定

pipenvをインストールしたら、PIPENV_VENV_IN_PROJECT環境変数をセットしておきます。pipenvで作成される仮想環境はデフォルトで~/.local/share/virtualenvsに格納されますが、この設定を有効にするとプロジェクトルートディレクトリの.venvディレクトリに仮想環境が作成される挙動に変更されます。特にPyCharmで開発するときはこの.venvディレクトリをデフォルトで読み込んでくれるため、この設定を有効にしておくことをオススメします。

お使いのシェルの設定ファイル(~/.bashrc~/.zshrcなど)を開き、以下の行を追加してください。追加したら端末を再起動するかsourceコマンドを使って設定を再読込してください。


~/.bashrc

export PIPENV_VENV_IN_PROJECT=1



その他ツールの導入

これから利用するGitDockerを導入しておいてください。ここでは本題から外れるため、詳細なインストール手順は省きます。


Pythonプロジェクトの初期化

ここからはいよいよPythonプロジェクトを作成していきます。


プロジェクトの作成

適当なディレクトリを作り、pipenv installコマンドを使ってプロジェクトを初期化します。このとき--pythonオプションを使うことでpyenvを使って自動的にそのバージョンをインストールしてそこから仮想環境を切り出してくれます。今回は無駄に最新版の3.7.0を使ってみましょう。「Python 3.7.0はインストールされていませんが、pyenvを使ってインストールしますか?」と聞かれるので、エンターキーを押します。後は終わるまで待つだけです。

$ mkdir sample_project

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

一連の作業が終わってからlsすると、PipfilePipfile.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のように範囲指定をすることもできます。


Pipfile

[[source]]

url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
django = "*"

[dev-packages]

[requires]
python_version = "3.7"


Pipfile.lockには依存関係にあるパッケージも列挙されています。また、パッケージのハッシュ値も記載されるようになっています。このハッシュ値はダウンロードされたパッケージそのものから生成されており、これによって異なる環境下でも同一のパッケージがインストールされることを保証することができます。


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にflake8mypyを採用することにした場合、以下のように-dオプションを付けてインストールします。

$ pipenv install -d flake8 mypy

もろもろをインストールしたら、PipfilePipfile.lockをaddしてcommitしておきましょう。

$ git add Pipfile Pipfile.lock

$ git commit -m "install django, flake8 and mypy"


Pythonスクリプトの実行

pipenvで作られるのは仮想環境であるため、pipenvでインストールしたパッケージを利用したPythonスクリプトを実行するためにはまずその仮想環境に入る必要があります。以下のコマンドで仮想環境に入れます。仮想環境に入るとプロンプトの先頭に仮想環境名が表示されるようになります。仮想環境を抜けるときはexitコマンドを利用します。

$ pipenv shell

(sample_project)$ python foo.py
(sample_project)$ exit


Docker化

ここからはDockerに載せるための設定をしていきます。


.dockerignoreの準備

まず余計なファイルを転送しないために.dockerignoreを書きましょう。特に.venvディレクトリは無視するようにします。その他不要なものも一緒に書いておくと良いでしょう。


.dockerignore

.mypy_cache/

.venv/
__pycache__/


Dockerfileの書き方

.dockerignoreが書けたらDockerfileを書きます。公式のPythonイメージから作っていきます。プログラムを多少変えただけなのにいちいち依存パッケージのインストールから始めるのは無駄なので、まずはPipfilePipfile.lockだけを転送し、先にパッケージをインストールしてから、Pythonスクリプトをコピーして作成したアプリケーションを実行するようにします。

また、Python公式イメージにはデフォルトでpipenvが含まれていないため、Dockerfile内でインストールする必要があります。pipenvがインストールできたら、依存パッケージをインストールします。このとき、開発用のパッケージは導入する必要が無いので-dオプションは付けないでおきます。

また、--systemオプションを使うことで、システムデフォルトのpipを使ってパッケージをインストールしてくれます。Dockerで使う場合はこのオプションを付けると良いでしょう。逆にそうしないといちいちpipenvで作成した仮想環境に入る手間が加わってしまいます。


Dockerfile

FROM python:3.7

ENV PYTHONUNBUFFERED=1

WORKDIR /usr/src/app

COPY Pipfile Pipfile.lock ./

RUN pip install pipenv \
&& pipenv install --system

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開発環境を快適にしてみましょう!