Edited at

pip関連ツールでPythonのパッケージ管理を楽にする


追記


2017.11.11 Pipenvの紹介

まさにbundlerやpackage.json的な pipenv というツールが出てきました。現在はそちらを利用するとよいでしょう。

pipenv公式ドキュメント(日本語訳)


2018.12.20 Poetryの紹介

ライブラリ開発者にはPipenvよりもPoetryのほうが良さそうです。個人のブログのほうに記事を書きました。

Poetryを使ったPythonパッケージ開発からPyPI公開まで - PYTHONIC BOOM BOOM HEAD


はじめに

最近、いろんなツールの存在を知ってパッケージ管理方法を改めたのでメモ。

たまにtwitterとかで「Pythonってpackage.json的なのとかbundler的なの無いの?requirements.txtで管理するくらいしかできないの?」って目にするけど、以下のツールたちを使えばある程度不満は解消されるかもしれません。

なお、pip についてはインストール済みという前提で使い方等は割愛しますが、良さそうなまとめを見つけたので参考文献のほうに載せておきます。また、python標準のvenvが使えるならそれを、そうでない場合はvirtualenvの使用を推奨します。


全てのvenv環境で使いそうなコマンドラインツールすらもグローバルな環境に入れたくない(pipsi)

pipsi

flake8 や pip-tools など、今後作るであろうvenv環境全てで使いそうなパッケージは今までグローバルなpython環境にインストールしてきた。が、pipsiを使えば、~/.local 以下にvenvを作成しそこにインストールしたうえで ~/.local/bin への symlink を貼ってくれる。


インストール

githubのREADMEにあるとおりなんですが、get-pipsi.py を curl で取ってきて実行するだけ。

curl https://raw.githubusercontent.com/mitsuhiko/pipsi/master/get-pipsi.py | python

実際に実行した結果

$ curl https://raw.githubusercontent.com/mitsuhiko/pipsi/master/get-pipsi.py | python

% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2805 100 2805 0 0 4874 0 --:--:-- --:--:-- --:--:-- 4869
Installing pipsi
New python executable in /Users/kk6/.local/venvs/pipsi/bin/python2.7
Also creating executable in /Users/kk6/.local/venvs/pipsi/bin/python
Installing setuptools, pip, wheel...done.
You are using pip version 7.0.3, however version 7.1.2 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
Collecting pipsi
86
Using cached pipsi-0.9-py2-none-any.whl
Collecting virtualenv (from pipsi)
Using cached virtualenv-13.1.2-py2.py3-none-any.whl
Collecting Click (from pipsi)
Using cached click-5.1-py2.py3-none-any.whl
Installing collected packages: virtualenv, Click, pipsi
Successfully installed Click-5.1 pipsi-0.9 virtualenv-13.1.2
Installed pipsi binary in /Users/kk6/.local/bin

============================================================

Warning:
It looks like /Users/kk6/.local/bin is not on your PATH so pipsi will
not work out of the box. To fix this problem make sure to
add this to your .bashrc / .profile file:

export PATH=/Users/kk6/.local/bin:$PATH

============================================================

pipsi is now installed.

親切にも .local/bin へのパスが通ってないよって教えてくれてるので言われたとおりにする。

export PATH=/Users/kk6/.local/bin:$PATH

これで pipsi が使えるようになる。あとは

$ pipsi install pip-tools

とかすれば、グローバルな環境にインストールしたのと同じ感覚でどこでも pip-tools が使えるようになる。(pip-compile は pip-tools 付属のコマンド)

$ which pip-compile

/Users/kk6/.local/bin/pip-compile


あくまでもコンソールスクリプトが付属しているものに限る

あくまでも /usr/local/bin に入るはずのものを ~/.local/bin に入れてくれるものなので、そもそもコンソールスクリプトが存在しないパッケージはインストールされない(インストール完了後に自動的にアンインストールされる)

$ pipsi install flake8-import-order

Running virtualenv with interpreter /Users/kk6/.local/venvs/pipsi/bin/python2.7
Using real prefix '/usr/local/Cellar/python/2.7.10_2/Frameworks/Python.framework/Versions/2.7'
New python executable in /Users/kk6/.local/venvs/flake8-import-order/bin/python2.7
Also creating executable in /Users/kk6/.local/venvs/flake8-import-order/bin/python
Installing setuptools, pip, wheel...done.
You are using pip version 7.0.3, however version 7.1.2 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
Collecting flake8-import-order
Using cached flake8_import_order-0.6.1-py2.py3-none-any.whl
Collecting pep8 (from flake8-import-order)
Using cached pep8-1.6.2-py2.py3-none-any.whl
Installing collected packages: pep8, flake8-import-order
Successfully installed flake8-import-order-0.6.1 pep8-1.6.2
Did not find any scripts. Uninstalling.


pip-tools を使う

pip-tools


インストール

$ pip install pip-tools


pip-compile

pip-compile コマンドは、.in ファイルを読み込んで、必要なパッケージ群を .txt ファイルへと吐き出してくれるコマンド。

まず、インストールしたいパッケージ名を requirements.in ファイルに記載する。


requirements.in

requests


コマンドを実行すると以下のように表示され、この内容通りの requirements.txt が作成される。

$ pip-compile requirements.in

#
# This file is autogenerated by pip-compile
# Make changes in requirements.in, then run this to update:
#
# pip-compile requirements.in
#
requests==2.8.1

また、開発用のパッケージとそれ以外といった具合に requirements ファイルを分割して管理したいという場合は以下のような構成でファイルを用意する。個人的な好みでディレクトリを作ってその中に配置したけど、ディレクトリを作らずにフラットに requirements.inrequirements-dev.in を並べてもいいと思う。

requirements

├── base.in
└── dev.in

内容はこんな感じ。


requirements/base.in

django



requirements/dev.in

django-debug-toolbar


pip-compile.in ファイルを一括指定出来ないのでひとつずつcompileしていく。

$ pip-compile requirements/base.in

#
# This file is autogenerated by pip-compile
# Make changes in base.in, then run this to update:
#
# pip-compile base.in
#
django==1.8.6

$ pip-compile requirements/dev.in
#
# This file is autogenerated by pip-compile
# Make changes in dev.in, then run this to update:
#
# pip-compile dev.in
#
django-debug-toolbar==1.4
django==1.8.6 # via django-debug-toolbar
sqlparse==0.1.18 # via django-debug-toolbar

これで、以下のようにそれぞれに対応する .txt ファイルが生成される。

requirements

├── base.in
├── base.txt
├── dev.in
└── dev.txt

なお、バージョンをある程度範囲を絞ったり指定したい場合は以下のように書く。


requirements.in

django>=1.8.0,<1.9.0

tweepy==3.3.0
ansible>=1.8.0

また、αでもβでもいいからとにかく最新のが欲しい!という場合は --pre オプションを指定する。

$ pip-compile --pre requirements/base.in

#
# This file is autogenerated by pip-compile
# Make changes in base.in, then run this to update:
#
# pip-compile base.in
#
django==1.9b1

requirementsにヘッダーが不要な場合は --no-heaer を指定する

$ pip-compile --no-header requirements/base.in

django==1.8.6

また、デフォルトで --annotate オプションが有効なため、依存パッケージには以下のようにコメントが付く。

django-debug-toolbar==1.4

django==1.8.6 # via django-debug-toolbar
sqlparse==0.1.18 # via django-debug-toolbar

コメントが不要な場合は --no-annotate オプションを指定する

$ pip-compile --no-annotate requirements/dev.in

#
# This file is autogenerated by pip-compile
# Make changes in dev.in, then run this to update:
#
# pip-compile dev.in
#
django-debug-toolbar==1.4
django==1.8.6
sqlparse==0.1.18


pip-sync

pip-sync コマンドは、指定した requirements ファイルを読み込んで、パッケージをファイルの内容に同期してくれる。環境に未インストールならインストールしてくれて、バージョン違いなら指定のバージョンに、ファイルに未記載のパッケージがあれば削除してくれる。

pip-sync は一括で指定できる。逆に、一個ずつやると pip-sync requirements/dev.in したときに base.in で入れたのが全部アンインストールされるので注意。requirementsファイルが一つの時はもちろん気にしなくてOK。

$ pip-sync requirements/*.txt

Collecting django-debug-toolbar==1.4
Using cached django_debug_toolbar-1.4-py2.py3-none-any.whl
Collecting sqlparse==0.1.18
Using cached sqlparse-0.1.18.tar.gz
Collecting django==1.8.6
Using cached Django-1.8.6-py2.py3-none-any.whl
Building wheels for collected packages: sqlparse
Running setup.py bdist_wheel for sqlparse
Stored in directory: /Users/kk6/Library/Caches/pip/wheels/f0/78/f3/329eab6612e4c39684d45feeab48be7b992b1131a2b504339b
Successfully built sqlparse
Installing collected packages: sqlparse, django, django-debug-toolbar
Successfully installed django-1.8.6 django-debug-toolbar-1.4 sqlparse-0.1.18

なお、 --dry-run オプションで何がインストールされるか確認できる。

$ pip-sync --dry-run requirements/*.txt

Would install:
django-debug-toolbar==1.4
sqlparse==0.1.18
django==1.8.6


古くなったパッケージをインタラクティブに管理したい(pip-review)

pip-review

pip コマンドは、以下のようにすると古くなったパッケージがあればそれを表示してくれる。

$ pip list --outdated

setuptools (Current: 17.0 Latest: 18.5 [wheel])

ただ、これは教えてくれるだけでアップグレードはしてくれない。pip-compileのほうも、最新のバージョンをrequirementsファイルに出力してはくれるけど、例えばこれとこれはアップグレードしたい、でもこっちは今のバージョンを使いたい…となると requirements.in ファイルを編集してバージョンを指定しないといけない。編集したら下で、後々アップグレードするつもりだったのに、 .in ファイルのバージョン指定を外すのを忘れると、いつまでたっても最新版が落ちてこないということにもなりかねない。

そんな、とりあえず今だけ最新版にしたくないパッケージが一部あるんだけど、一個ずつ聞いてくれない?という時に、pip-review を使うとインタラクティブに管理できる。

この pip-review は、もともと pip-tools の一機能だったのだけど、ある時不要と判断されて削除された。でも復活を望む声が結構あったようで、ある時 pip-tools を fork して別プロジェクトとして派生させたらしい

$ pip install pip-review

オプションはこんな感じ。

$ pip-review -h

usage: pip-review [-h] [--verbose] [--raw] [--interactive] [--auto]
[--editables] [--local]

Keeps your Python package dependencies pinned, but fresh.

optional arguments:
-h, --help show this help message and exit
--verbose, -v Show more output
--raw, -r Print raw lines (suitable for passing to pip install)
--interactive, -i Ask interactively to install updates
--auto, -a Automatically install every update found
--editables, -e Also include editable packages in PyPI lookup
--local, -l If in a virtualenv that has global access, do not output
globally-installed packages

こんな風に、インタラクティブに管理できる。

$ pip-review -i

Django==1.9b1 is available (you have 1.8.6)
Upgrade now? [Y]es, [N]o, [A]ll, [Q]uit

ちなみに上記の通り、pip-reviewをフォークした時点ではpip-toolsの --pre オプションがまだ無かったのか、pip-reviewはPyPIの最新版がβバージョンならそれを参照してしまう模様。ただ、今週中にもPRが作成される模様


パッケージが古くなったら自動でお知らせしてほしい(Requires.io)

Requires.io

リポジトリのrequirements.txtを監視して、パッケージに更新があれば教えてくれるWebサービス。通知方法はメールとPull Request(Githubのみ)。

他にも、画面上でパッケージの依存関係が一覧表示できたり、各パッケージのリポジトリへのリンクだったりライセンスだったりが見れる。


依存パッケージをまとめて削除(pip-sync or pip-autoremove)

pip-autoremove

.in ファイルからパッケージを削除して pip-compile & pip-sync すれば依存パッケージもろとも削除してくれる。

ただし、もし pip-tools を使わずに、例えばお試しで直接 pip install で入れたパッケージが気に入らなくて削除したいんだけどなんか色々依存パッケージ入れられてわけわかんなくなったなんてときに、コマンド一発でもろもろ削除したいなら pip-autoremove を使う。

$ pip install pip-autoremove

$ pip-autoremove Flask Sphinx -y

Sphinx 1.2.2 (/tmp/pip-autoremove/.venv/lib/python2.7/site-packages)
Jinja2 2.7.3 (/tmp/pip-autoremove/.venv/lib/python2.7/site-packages)
MarkupSafe 0.23 (/tmp/pip-autoremove/.venv/lib/python2.7/site-packages)
Pygments 1.6 (/tmp/pip-autoremove/.venv/lib/python2.7/site-packages)
docutils 0.12 (/tmp/pip-autoremove/.venv/lib/python2.7/site-packages)
Flask 0.10.1 (/tmp/pip-autoremove/.venv/lib/python2.7/site-packages)
Werkzeug 0.9.6 (/tmp/pip-autoremove/.venv/lib/python2.7/site-packages)
Jinja2 2.7.3 (/tmp/pip-autoremove/.venv/lib/python2.7/site-packages)
MarkupSafe 0.23 (/tmp/pip-autoremove/.venv/lib/python2.7/site-packages)
itsdangerous 0.24 (/tmp/pip-autoremove/.venv/lib/python2.7/site-packages)
...


パッケージの依存treeが見たい(pipdeptree)

pipdeptree

pip-compile で生成された requirements.txt とか見ればある程度依存関係とか書いてくれてるけど、コマンド叩いて依存関係を階層的に確認したい場合は便利かもしれない。

$ pip install pipdeptree

$ pipdeptree

...
argparse==1.2.1
wsgiref==0.1.2
peep==1.3
ansible==1.7.1
- paramiko [installed: 1.15.1]
- pycrypto [required: >=2.1, installed: 2.6.1]
- ecdsa [required: >=0.11, installed: 0.11]
- Jinja2 [installed: 2.7.3]
- MarkupSafe [installed: 0.23]
- PyYAML [installed: 3.11]
- setuptools
- pycrypto [required: >=2.6, installed: 2.6.1]
dopy==0.3.0
- requests [required: >=1.0.4, installed: 2.4.3]


PyPI パッケージレベルで検索したい(yolk)

yolk

pip search django とかすると、PyPIのwebページで検索するのと同じで、djangoに関連するパッケージがずらっと表示される。そうではなくて、django今どんなバージョンがアップされてるんだろう、というのが知りたい場合は yolk が便利。

$ pip install yolk

$ yolk -V django

Django 1.9b1
Django 1.9a1
Django 1.8.6
Django 1.8.5
...
Django 1.1.4
Django 1.0.4


npm init 風に setup.py を自動生成(pip-init)

pip-init

demo

node の npm init で package.json を生成する感じで、setup.py の雛形を生成してくれる。

setup.py の書き方からPyPIへ登録とかその他いろいろ覚えるなら PyPIデビュー2015 を参考にするとよいと思います。


参考文献