Pythonパッケージ作りの初歩の初歩

私は、ライトなPythonユーザーなので、pipコマンドでPyPIからパッケージを取ってきてインストールはやるのですが、いまいち、pip/easy_install/distutils/setuptools/setup.pyが一体なんなのか、どう違うのかなど、よくわからないまま、なんとなくpipだけを使ってきていました。

PyPIで何かパッケージを公開することも、特段、他人にパッケージを配布することも、なかったのですが、「パッケージ」について調べたので、ブログにまとめておくことにしました。

と、調べ始めたところ・・・

Python パッケージ管理技術まとめ (pip, setuptools, easy_install, etc)がめちゃくちゃよくまとまってます。
最初これを読んで、情報量が多すぎて圧倒されてしまったのですが、一周回って戻ってきたら、とてもわかりやすくまとまっていて、良いです!!

(自分の記事の存在価値がほぼなくなった・・・)

用語「パッケージ」

最初、私が混乱していた用語が「パッケージ」です。

パッケージ (package) は、Python のモジュール名前空間を “ドット付きモジュール名” を使って構造化する手段です。

https://docs.python.jp/3/tutorial/modules.html#packages

という意味でのパッケージと、もう一つあります。

重要な注意: この文脈で「パッケージ」とは、distribution (つまり、インストールされるひとかたまりのソフトウェア)の同義語であり、Python ソースコード内で import する package (モジュールコンテナ)を指すのではない。Python コミュニティでは、distribution を表すのに「パッケージ」の語を用いるのが普通だ。 “distribution” という用語はしばしば避けられる。というのは、Linux distribution だとか、あるいは Python 自身のような他のより大きなソフトウェアの distribution と混同しやすいからだ。

http://python-packaging-user-guide-ja.readthedocs.io/ja/latest/installing.html

「配布物としてのパッケージの中に、モジュールコンテナとしてのパッケージとその他もろもろ(設定やReadmeやテストなど)を作っておいて、pipでsetuptoolsを利用して、便利にインストールする」という理解でしょうか。

ということで、配布物としてのパッケージ作り方については、
(インターン向けに書いた)Pythonパッケージを作る方法 - Qiita
がとてもわかりやすかったです。

pip

さらに、思い違いをしていたことがありました。それは、pipコマンドでできることです。
PyPIから取ってきてインストールしてくれるのみのコマンドだと思っていました(なんとなくcondaがanaconda cloudからとってきてくれるから、対象サーバーごとのコマンドかと思っていた)。
しかし、そんなことはなくて、ローカルにある自分で作った(配布物としての)パッケージに対しても使えるということです*1

(お前は一体今さら何をいっているんだと怒られそうな予感)

例えば、ローカルにあるパッケージのディレクトリを指定して、

pip install ./mypakage

のようなことをすれば、PyPIに存在していないmypakageをインストールすることができます(./とかをつけて、パスっぽい感じにしてあげるところがポイント)*2

  • -eオプションを使えば、インストールしたふう*3になるそうです(つまり、パッケージをアップグレードしなくても、パッケージのディレクトリの変更が反映されます)
  • ローカルのパッケージだけじゃなくて、gitのリポジトリも指定できるみたいです。
pipと配布物としてのパッケージをつなぐもの

pipはパッケージインストール&管理ツールなのですが、pipがあるおかげで、パッケージの依存関係を解決できて必要なパッケージも一緒にインストールできたり、パッケージのバージョンを指定してインストールできたり、アンインストールできたり*4、アップグレードしたりできるようです。

こういったpipの恩恵に預かるには、ある一定の作法(?)に従って、パッケージに関する情報を記述しておく必要があるようです。

それが、setup.pyというスクリプトであったり、setup.cfg、README.rst、MANIFEST.inといったファイルになるそうです(この辺は、PyPIにパッケージを登録するかしないかで、どのファイルが必要かは変わりそう。)

setup.pyなどに何を記述すべきかというのは、Packaging and Distributing Projects — Python Packaging User Guideが一次情報になるでしょうか。再掲になりますが、(インターン向けに書いた)Pythonパッケージを作る方法 - Qiitaがsetup.pyについて書かれているのがわかりやすかったです。

setup.pyは、現在では、setuptoolsのsetupを呼び出すことをするようです。昔は(?)、ここで使われるツールが、setuptoolsではなくて、distutilsだった模様(今もsetuptoolsの内部的に利用している??)。

その他事項
  • 試しに何かやってみようと思った場合は、venv等で仮想環境作ってから、pip installすることをおすすめします。
  • PyPIは「パイ・ピー・アイ」と読むらしい。PyPyと区別をするためらしい。
  • PyPIに自分のパッケージを登録することもpipのコマンドで行います。(テストPyPIサーバーというものがあって、登録の練習ができるみたいなので、いつかの日のために今度やってみたいですね。)PyPIデビューしたい人の為のPyPI登録の手順 - Qiita
  • 例えばflake8のように、pipで入れると、コマンドになってくれるやつもありますが、setup.pyの中のsetupにentory_pointsを書いて(ちゃんと動くようにやれば)、できるようです。
  • C/C++の拡張もできるみたいです。
  • NumPyは、distutils(Packaging (numpy.distutils) — NumPy v1.13 Manual)があるらしく、Fortran拡張もいける?*5
  • 自前でPyPIっぽいものが欲しい場合は、devpipypiserverといったパッケージがあるらしい。
  • git等の場合は、version等も指定してインストールとかもできる。
(やりたかったこと)

自分で使うようにモジュールコンテナとしてのパッケージを作っていたのですが、あるディレクトリで利用したいときはgit cloneして、また、別のディレクトリで使いたいときは、またgit cloneして・・・と、毎回git cloneするのがめんどくさくて、何かうまい方法がないのかなと思ったのが、今回、パッケージについて調べるきっかけでした。

実態を一つにしておいて、sys.pathに追加するといったことも検討しましたが、これも毎回スクリプトの先頭で追加の部分を書かないといけないのかなーと思っていました。

特に仮想環境等を用意せずにホームディレクトリやどこでもでipythonを起動した時に、import numpyするように、自分のパッケージを利用できたらいいなと思っていたのでした。

その目的を達成する方法としては、自分でsetup.pyまで書いて置いて、そのディレクトリ(かgitリポジトリ)に対して、pip install ./mypackage(-eオプションをつけるかどうかは、やりたいことによると思いますが)でインストールする(できる)というのが、一つの答えなのかなと思いました。

今後の課題
  • setup.pyの書き方をきちんと理解する
    • 依存関係の書き方
    • エントリーポイント
  • PyPIデビュー?

*1:python setup.py installを使ったことがないpip世代

*2:他にもいろいろあるようです。https://packaging.python.org/tutorials/installing-packages/

*3:シンボリックリンク

*4:python setup.py installでインストールできても、python setup.py uninstallはできない。一手間必要。

*5:これはドキュメントのページ発見しただけ