Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Python アプリ依存パッケージ管理 ベストプラクティス集

More than 3 years have passed since last update.

はじめに

Python のパッケージ管理は複雑です。

ある程度 Python に詳しい方でも、そのすべてを把握しきれていない方は多いのではないでしょうか。

私もその一人です。

そこで、本記事では Python アプリケーションにおける依存パッケージ管理方法のベストプラクティスをまとめます。

なお、ライブラリにおける依存パッケージ管理は扱わないものとしますが、参考になる点はあるかもしれません。

ベストプラクティス?

本記事のタイトルには、「ベストプラクティス」とあります。また、「ベストプラクティスをまとめます」と前述しました。

これはどういうことでしょうか。

答えは簡単です。Python の依存パッケージ管理方法には、唯一のベストプラクティスがないのです。

よって、本記事ではベストプラクティスを一つに決めることをせず、様々な方法を紹介することにしました。

また、新しい方法を発見した場合は随時追記し、本記事が常にベストプラクティス集であることを目指します。

ベストプラクティス集

Traditional requirements.txt

$ pip install flake8
$ pip freeze > requirements.txt
$ cat requirements.txt
flake8==2.5.4
mccabe==0.4.0
pep8==1.7.0
pyflakes==1.0.0
$  pip install -r requieremnts.txt

必要なパッケージをインストールした後、インストールされているすべてのパッケージとそのバージョンを出力します。

あまり便利ではなく、ベストプラクティスとは言えないかもしれません。ですが、最もよく見かける方法であるため、ここに挙げました。

requirements.txt

$ echo "python-dateutil" > requirements.txt
$ echo "flake8" > testRequirements.txt
$ pip install -r requirements.txt -r testRequirements.txt
$ pip freeze > constraints.txt
$ cat constraints.txt
flake8==2.5.4
mccabe==0.4.0
pep8==1.7.0
pyflakes==1.0.0
python-dateutil==2.5.3
six==1.10.0
$ pip install -r requirements.txt -r testRequirements.txt -c constraints.txt

シンプルな requirements.txt のみの方法を constraints.txt で補強した、より実践的な方法です。

Constraints File に書かれた情報は、インストール対象には追加されずバージョン制約のみを適用されます。詳しくは解説記事 を参照してください。

pip freeze の結果を書き込むのではなく、手動で requirements.txt を記述します。記述するのは、直接依存するパッケージのみで、バージョンの指定も必要最低限のみにします。多くの場合は、pip install する代わりに、requirements.txt に記述する程度で十分です。

requirements.txt に詳細な情報を記述しない代わりに、pip freeze の出力を constraints.txt に書き込むことで、バージョンを固定します。

また、必要に応じて複数ファイルに分割することで、グルーピングを行うことが出来ます。

なお、constraints.txt に書かれた内容がインストールされることはないため、requirements.txt をグループ分けした場合にも、constraints.txt は単一ファイルのままで問題ありません。

setup.py + constraints.txt

from setuptools import setup

requires = ['python-dateutil']
extras = {'test': ['flake8']}

setup(
    name='app',
    install_requires=requires,
    test_requires=extras['test'],
    extras_require=extras,
)
$ pip install -e .[test]
$ pip freeze > constraints.txt
$ cat constraints.txt
app==0.0.0
flake8==2.5.4
mccabe==0.4.0
pep8==1.7.0
pyflakes==1.0.0
python-dateutil==2.5.3
six==1.10.0
$ pip install -e .[test] -c constraints.txt

後述する setup.py + requirements.txt を、Constraints File を用いて改善したパターンです。

setup.py の install_require, extra_requires を用いて依存パッケージを記述します。

なお、extra_requires を extra_requires={'test': ['flake8']} のように指定した場合、pip install -e .[test] のようにして 指定したパッケージをインストールできます。

Constraints File は比較的新しい機能であるため、この方法を採用しているプロジェクトはあまりありませんが、いま採用するなら 次のパターンよりも、このパターンのほうが良いのではないでしょうか。

setup.py + requirements.txt

from setuptools import setup

requires = ['python-dateutil']
extras = {'test': ['flake8']}

setup(
    name='app',
    install_requires=requires,
    test_requires=extras['test'],
    extras_require=extras,
)
$ pip install -e .[test]
$ pip freeze > requirements.txt
$ cat requirements.txt
app==0.0.0
flake8==2.5.4
mccabe==0.4.0
pep8==1.7.0
pyflakes==1.0.0
python-dateutil==2.5.3
six==1.10.0
$ pip install -r requirements.txt

pip にまだ Constraints File がなかった頃によく見られた手法で、著名なパッケージ等でも使用されています。

しかし、この場合は test の対象も requirements.txt に書き込まれるため、本番環境でもテスト用のパッケージがインストールされてしまいます。

requirements.txt 分割することで対応はできますが、Constraints File を使用したほうが簡潔です。

pip-tools

$ pip install pip-tools
$ echo "python-dateutil" > requirements.in
$ echo "flake8" > testRequirements.in
$ pip-compile --output-file requirements.txt requirements.in
#
# This file is autogenerated by pip-compile
# To update, run:
#
#    pip-compile --output-file requirements.txt requirements.in
#

python-dateutil==2.5.3
six==1.10.0               # via python-dateutil
$ pip-compile --output-file testRequirements.txt testRequirements.in
#
# This file is autogenerated by pip-compile
# To update, run:
#
#    pip-compile --output-file testRequirements.txt testRequirements.in
#  

flake8==2.5.4
mccabe==0.4.0             # via flake8
pep8==1.7.0               # via flake8
pyflakes==1.0.0           # via flake8
$ pip-sync requirements.txt testRequirements.txt
$ pip freeze
click==6.6
first==2.0.1
flake8==2.5.4
mccabe==0.4.0
pep8==1.7.0
pip-tools==1.6.5
pyflakes==1.0.0
python-dateutil==2.5.3
six==1.10.0

pip-tools というサードパーティのツールを使用するパターンです。

pip-compile は、requirements.in から requirements.txt を作成します。

pip-sync は 必要なパッケージをインストールするだけでなく、不要なパッケージを自動で削除します。 もちろん、requirements.in に pip-tools を記述していなくても、pip-sync によって pip-tools が削除されることはありません。

また、ファイルを分割することによってグルーピングすることができます。

まとめ

Python アプリで依存パッケージを管理するときの、幾つかのベストプラクティスを紹介しました。

現状では、どれがベストということはできないため、プロジェクトの性質に合わせて、最適なものを選ぶしかありません。

できるだけ近いうちに、いずれか方法がデファクトスタンダードとなることを願います。

fe2o3
ソフトウェア開発、教育、情報セキュリティ事業を通して、人とコンピュータのよい関係をつくる企業です。CTF大会開催のトータルソリューション「CTFKit」といった自社開発や、スタートアップに特化した受託開発など事業展開しています。
https://fe2o3.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away