Edited at

pythonのsetup.pyについてまとめる


はじめに

Pythonのパッケージ管理や配布について。

どうやって他人からも未来の自分からも再利用しやすいパッケージが書けるのか分からなかったので調べた。結論、2018年8月時点ではsetup.pyを書くのが良さそうと判断。以下に調べたことをまとめる。Python3を対象とする。


参考

公式がパッケージや配布について解説したドキュメント

https://packaging.python.org/

日本語の詳しいサイト(最終更新が2016年なのでちょっと古い)

http://www.yunabe.jp/docs/python_package_management.html

setuptoolsの公式ドキュメント

https://setuptools.readthedocs.io/en/latest/setuptools.html


この記事で作るファイルの配置

setup.py初めて作る人が参考になるように記載。

setup.py関連のファイルは全てプロジェクトのルートに配置する。

project-folder

+ your_great_package
| + __init__.py
| + awsome_module.py
+ setup.py
+ setup.cfg


setup.pyを書くメリット


特別なツールが不要

Pythonをインストールする際に標準で付属するpipを使ってコードを他人と共有できる。つまり特別な準備が不要。


慣れ親しんだ方法でコードを共有できる

setup.pyを書くと

pip install foopackage

と打ち込むだけで依存関係も含めた環境を簡単に構築できる。

当然ながらこのようにしてインストールしたパッケージはコードに

from foopackage.barmodule import hogehoge

と記述することでどこからでも参照できる。


setup.pyの書き方


シンプルな記述方法


setup.py

from setuptools import setup

setup(
name="パッケージの名前",
version="パッケージのバージョン(例:1.0.0)",
install_requires=["packageA", "packageB"],
extras_require={
"develop": ["dev-packageA", "dev-packageB"]
},
entry_points={
"console_scripts": [
"foo = package_name.module_name:func_name",
"foo_dev = package_name.module_name:func_name [develop]"
],
"gui_scripts": [
"bar = gui_package_name.gui_module_name:gui_func_name"
]
}
)


設定内容は全てsetup関数の引数として記述する。

install_requiresの引数として指定されたパッケージはpip install -e .した時に一緒にインストールされる。またextras_requiresに記述されたパッケージはpip install -e .[develop]等と指定することでインストールされる。entry_pointsに指定された関数はpip installした時に実行可能ファイルとして生成される。

 ※pip installでParmittionエラーが出たら、python -m pip installを試してみること

これはこれでよいのだが、いかんせん読みにくい。特に設定項目が増えてきた場合には。また設定を編集する場合ある程度プログラミングの素養が必要になる。環境構築担当者のスキルが無い場合、設定をいじらせるのがしんどい。

この問題はsetup.pyと同じディレクトリにsetup.cfgという設定ファイルを作ることによって解決できる。


setup.cfgを使ったsetup.pyの記述方法

setup.cfgを使うことで、setup.pyはこうなる。


setup.py

from setuptools import setup

setup()


続いてsetup.cfgはこうなる。


setup.cfg

# metadataセクションではパッケージのメタデータを定義する

# これらの値はpypiで公開した際に表示される。
# なおversion等、一部のキーはディレクティブの指定により外部ファイルから値を取得することができる
# https://setuptools.readthedocs.io/en/latest/setuptools.html#metadata
[metadata]
name = your_package
version = attr: src.VERSION
license = file: license.txt

# optionsセクションではパッケージの依存関係やpip installした時の動作を定義する
# 値が複数ある場合、インデントすることで1行に1つの値を書くことができる。
# https://setuptools.readthedocs.io/en/latest/setuptools.html#options
[options]
install_requires =
packageA
packageB

# optionの内、値のTypeがsectionのものは別セクションで記述する。
[options.extras_require]
develop =
dev_packageA
dev_packageB

[options.entry_points]
console_scripts =
foo = package_name.module_name:func_name
foo_dev = package_name.module_name:func_name [develop]
gui_scripts =
bar = gui_package_name.gui_module_name:gui_func_name


基本的な書き方はWindowsの.iniファイルに似ている。"["と"]"でセクションを表し、key=valueという形式で値を指定する。詳細な仕様についてはsetuptoolsのドキュメントを参照。

https://setuptools.readthedocs.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files


依存パッケージのバージョンを指定したい場合

setup.pyおよびsetup.cfgでは依存するパッケージのバージョンを指定することはできない。そこでconstraints.txtを作成し、そこにパッケージのバージョンを書き込む必要がある。

constraints.txtの作り方

pip freeze > constraints.txt

constraints.txtの使い方

pip install -e . -c constraints.txt

pipのバージョンによっては上記のコマンドでエラーが出る。

C:\prj\myprj > pip install -e .[develop] -c constraints.txt

Could not satisfy constraints for 'myprj': installation from path or url cannot be constrained to a version

これはpipのバグのようなのでその内修正されるはず。

エラー文の内容はmyprjというパッケージがPYPIに登録されていないことに起因する。従ってmyprjパッケージをPYPIにアップするか、constraints.txtからmyprjの行を削除すればよい。


setup.pyに関連するパッケージの説明とリファレンス

以下の記述は豆。


setuptools

https://setuptools.readthedocs.io/en/latest/

今回のまとめの中心となるパッケージ。Python標準ではないが、pipを導入する際に標準でインストールされる。

公式ドキュメントにはsetuptoolsについて以下の通り記述されている。便利そうだし公式がわざわざ言及しているのだから、おそらくこれが標準に最も近いと思われる。


Most Python users will not want to use this module directly, but instead use the cross-version tools maintained by the Python Packaging Authority. In particular, setuptools is an enhanced alternative to distutils that provides:


  • support for declaring project dependencies

  • additional mechanisms for configuring which files to include in source releases (including plugins for integration with version control systems)

  • the ability to declare project “entry points”, which can be used as the basis for application plugin systems

  • the ability to automatically generate Windows command line executables at installation time rather than needing to prebuild them

  • consistent behaviour across all supported Python versions

Python公式より引用 - https://docs.python.org/3/library/distutils.html


(以下意訳。英語が得意な方、添削してくださるとありがたいです)

一般的なPythonユーザーはdistutilsモジュールを直接使わない方がよいでしょう。代わりにPyPA(Pythonの主要なパッケージをメンテナンスしているグループ)でメンテナンスしているツールを使うべきです。これらのツールの動作はバージョン非依存になるようにメンテナンスされています。setuptoolsはdistutilsの代替として特に広く使用されており、以下の機能を提供しています。


  • プロジェクトの依存関係を定義

  • リリース毎に含めるファイルを構成するための追加機能(バージョン管理システム統合用プラグインを含む)

  • プロジェクトのエントリーポイントを定義。これによりプラグイン構築のための基盤が提供される。

  • パッケージインストール時にWindowsコマンドラインで動作する実行可能ファイルを自動生成する機能。これによりパッケージインストール後にビルドする手間が省けます。

  • Pythonがサポートしている全バージョンにおける一貫した動作の保証


distutils

https://docs.python.org/3/library/distutils.html

python標準パッケージ。名前の通りコードを配布するために必要な機能が提供される。ただしユーザーがこのパッケージを直接参照することはほぼない。(公式ドキュメントにも「一般ユーザーは使うな」と書かれている)

上述したsetuptoolsはこのパッケージの機能を拡張したものである。


wheel

https://pythonwheels.com/

Pythonのパッケージ形式のこと。またファイルをwheel形式にパッケージングするためのツール名でもある。wheel形式はPEP376に定義されている。