Mac上のPython仮想環境をpipenv+pyenvへ移行してみた

Mac上のPython仮想環境をpipenv+pyenvへの組み合わせに移行した過程の記録です。pipenvにまつわる操作を幾つかまとめています。
2018.10.20

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

virtualenv設定管理の簡易化を主な目的として、Pythonの仮想環境をpyenv+pyenv-virtualenvからpipenvを中心とした状態へと移行を行ってみました。

2018/10/20 20:55 update: pipenvでのtoxを利用したテストにて、flake8を用いる場合のtox.ini記載例を追加しました。

pipenvについて

Pythonの仮想環境管理を行うライブラリです。仮想環境設定そのものはpipとvirtualenvを通して行いますが、生成されるPipfileとPipfile.lockを適切に管理することで、pipとvirtualenvを直接操作するケースに比べて負担を大幅に減らすことができます。

https://github.com/pypa/pipenv

pyenv-virtualenvとの差異

pyenvを元にPythonの仮想環境を管理するライブラリですが、pipenvと比べて以下の大きな挙動差異があります。

  • pyenv-virtualenvは、仮想環境を複数作成した場合そのうちの一つを選択する
  • pipenvは、仮想環境を作成する度に古い仮想環境を削除して新しい仮想環境を作り直す

複数の異なるPython仮想環境を随時切り替えての動作等が必要である場合は、pyenv-virtualenvの利用が好ましいと思われます。

pipenvの設定

pipenvは仮想環境生成時に

  • 仮想環境で用いるPythonのインストール
  • 仮想環境で用いるPyPIライブラリのインストール

の2つを行います。Pythonのインストールにはpyenvを利用します。

pyenvの導入

方法は以下の様に複数存在しますが、導入後に正常動作している状態であれば何れの手段でも問題ありません。

homebrew経由

% /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
% brew install pyenv
% echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
% exec $SHELL -l

anyenv経由

% brew install anyenv
% echo 'eval "$(anyenv init -)"' >> ~/.bash_profile
% exec $SHELL -l
% anyenv install pyenv
% echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
% exec $SHELL -l

公開リポジトリからのclone

% git clone https://github.com/yyuu/pyenv.git ~/.pyenv
% echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
% exec $SHELL -l

pipenvの導入

方法は以下の様に複数ありますが、導入後に正常動作している状態であれば何れの手段でも問題ありません。

homebrew経由

% brew install pipenv
% echo 'eval "$(pipenv --completion)”' >> ~/.bash_profile
% exec $SHELL -l

pip経由

% pip install --user pipenv
% echo 'eval "$(pipenv --completion)”' >> ~/.bash_profile
% exec $SHELL -l
# pipenvが正常に使えない場合
% echo "export PATH=\$PATH:\"$(python -m site --user-base)"/bin\" >> ~/.bash_profile
% exec $SHELL -l

pyenv-virtualenvでの自動切り替え無効化

既に作業ディレクトリ内をpyenv-virtualenvにて設定していた場合の対処です。

% cd path/to/dir
% python -V #pipenvで指定するバージョンを確認しておく
Python 3.6.5
% rm .python-version

pipenvによる仮想環境の設定

確認しておいたバージョンとパッケージにてインストールします。バージョンは3.6.5を直接指定しました。

% pipenv install -r requirements.txt --python 3.6.5

指定バージョンのPythonがpyenvで未インストールの場合は、追加インストールするか選択を求められます。インストールする場合はpyenvによるインストールとして扱われます。

基本操作

#インストールした仮想環境に入る
% pipenv shell
#依存ライブラリのアップデート確認
% pipenv update --outdated
#依存ライブラリのアップデート
% pipenv update
% pipenv update <package>
#ライブラリを追加インストールする
% pipenv install <package>
% pipenv install --dev flake8 #開発環境のみで使うライブラリの場合は--devフラグをつける
#セキュリティの脆弱性を確認する
% pipenv check

#仮想環境を終了させる
% exit

テストの実行

今回はtoxを利用する前提です。

2018/10/20 20:55: flake8を用いたケースを追記しました。

#テストライブラリの導入
% pipenv shell
% pipenv install --dev nose tox tox-pipenv

#tox用ファイル作成
% cat << EOF > setup.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from setuptools import setup
from setuptools import find_packages

def main():
    setup(
        name='Test',
        version='0.0.1',
        description='Test',
        author='your name',
        author_email='your_name@example.jp',
        packages=find_packages(),
        test_suite = 'nose.collector',
        zip_safe=False,
        include_package_data=True
    )


if __name__ == '__main__':
    main()
EOF

% cat << EOF > tox.ini
[tox]
envlist = flake8, py36

[testenv]
deps = pipenv 
commands=
    pipenv install --dev
    pipenv run nosetests -v
  
[testenv:flake8]
deps = pipenv
commands=
    pipenv install --dev 
    flake8 setup.py

[flake8]
ignore = E226,E302,E41

EOF
#テストの実行
% tox
% exit

Travis CI向け設定

Travis-CIの利用登録と、Githubリポジトリの同期等が必要になります。

Makefile

% cat << EOF > Makefile
init:
    pip install pipenv
    pipenv install --dev

test:
    pipenv run nosetests -v
EOF

.travis.yml

% cat << EOF > .travis.yml
language: python
python:
    - "3.6"

# command to install dependencies
install: "make"

# command to run tests
script:
    - make test
EOF

仮想環境の再現

生成済みのPipfileとPipfile.lockが存在する場合はsyncを実行します。

% ls
Pipfile Pipfile.lock
% pipenv sync --deploy

ディレクトリ移動にて自動で仮想環境に入る

shellオプションでの実行の手間を減らします。 (via:Run pipenv shell automatically)

% vim ~/.bash_profile
PROMPT_COMMAND='prompt'
precmd() { eval "$PROMPT_COMMAND" }
function prompt()
{
    if [ ! $PIPENV_ACTIVE ]; then
      if [ `pipenv --venv 2>/dev/null` ]; then
        export PIPENV_INITPWD="$PWD"
        pipenv shell
      fi
    elif [ $PIPENV_INITPWD ] ; then
      cd "$PIPENV_INITPWD"
      unset PIPENV_INITPWD
    fi
}

特定コマンドを簡潔に実行する

Pipfileにscriptsブロックを追加して、コマンドを指定します。

有効な指定例を参考に設定すると、より快適になると思われます。 (Qiita:2018年のPythonプロジェクトのはじめかた)

% vim Pipfile
[scripts]
# startコマンドでmain.pyを実行可能にする
start = "python main.py"
# lintコマンドでflake8を実行可能にする
lint = "flake8 ."
% pipenv run start

以上、個人的な環境設定にて動作検証も兼ねて行った結果をまとめました。