YAMAGUCHI::weblog

海水パンツとゴーグルで、巨万の富を築きました。カリブの怪物、フリーアルバイター瞳です。

wheelのありがたさとAnacondaへの要望

はじめに

こんにちは、Python界のラファエル・ナダルです。全豪オープンテニス、盛り上がりましたね。さて、先日次のようなエントリーを立て続けに書いたんですが、「なぜAnacondaに関しての記述がないのか」という突っ込みをもらったので、参照用にメモを残しておきます。

なおこの記事の作成にあたっては @aodag に数多くのアドバイスをいただきました。この場を借りて感謝。

TL;DR

condaの開発者はPyPAともっとコミュニケーションとってほしい。

前提

この記事はPythonを触り始めたばかりだけど、パッケージ管理ツール等々のスタンダードがどのようになっているかなど、経緯がわからず混乱した人向けに現状を把握するための補助として書いています。読み物として読んでもらえれば幸いです。

予備知識1:Pythonでの標準策定プロセス

PythonにはPEP(Python Enhancement Proposal)というものがあり、Pythonの言語仕様そのもの、実装の変更、3rd partyなどで提案されてから広く受け入れられた機能などを標準に取り込むための提案はこのPEPの形式で提出され、PEP Editorsに承認されたものが標準に取り込まれます。

つまりPEPで明文化された仕様が決定されれば、提案元の3rd partyパッケージの実装依存にならず、その仕様を汲みさらに便利な機能を追加した新たな3rd partyを安心して見守れる、というわけです。このサイクルによってPythonの標準機能は発展しています。

予備知識2:パッケージングツールとパッケージインストーラ

  • パッケージングツール : PyPIなどで配布するモジュールをパッケージするためのツール。パッケージ作者以外は特に意識することはありません。
    • eg) setuptools, wheel
  • パッケージインストーラ : PyPIなどで配布されているモジュールをインストールするためのツール。通常はこちらを使うことばかり。裏側で何をしているか意識することは少ないです。
    • eg) easy_install, pip

現在は setuptools, wheel, pipvirutualenv とともにPyPA (Python Packaging Authority)で管理されています。

予備知識3:Python Language Moratorium

PEP-3003で決められた言語仕様やビルトインを一定期間フリーズする宣言。Python2系からPython3系に移行するにあたり、言語仕様を一旦フリーズして、Python3.1からはPython2.6へ、Python3.2からはPython2.7へそれぞれ言語仕様をバックポートし、メジャーバージョン移行のための猶予期間をもたせるためのもの。また同時に、Python3.3以降はまた新たに言語仕様の変更や新しいビルトイン等を導入可能とも定めています。

wheelのありがたさ

wheel以前

3rd partyライブラリである setuptools が利用していた egg と呼ばれる形式を使ってバイナリパッケージ(C/APIを利用したpure Pythonでないパッケージを事前ビルドしたもの)を配布していました。この egg 形式はPEPで策定されたものではなく、setuptools が独自に決めたものでした。標準パッケージである distutils や先の setuptoolssetup.py sdist で生成する sdist はソースパッケージの配布形式であり、バイナリパッケージに関してはサポートしていません。(ところで実は現時点でも sdist の仕様が実は決まっていないというのは後で触れます)*1

一方で pip が標準になる以前、は、PyPIにあるパッケージのインストールは easy_install コマンドで行われていました。easy_install コマンドは setuptools もしくはそれをフォークした distribute (のちに setuptoolsにマージ)をインストールすると、そのエントリーポイントのコマンドとして利用できました。しかし後発の pip がパッケージを利用するだけの人からすると便利だったので、徐々に pip のシェアが伸び、パッケージインストールのためのデファクトスタンダードとなり、のちにPythonの標準配布に pip を同梱させる “ensure-pip” のためのPEPが策定されます。

ところがここで問題がありました。egg はすでにデファクトになっていたものの、作成した egg はメジャーバージョンの違いはおろか、マイナーバージョンが違っただけで動作しなかったため、パッケージ作成者は細かなバージョン単位での egg を作成する必要がありました。利用者も自分が利用しているPythonのマイナーバージョンにあった egg がないとインストールすることができませんでした。理由はわからないですが、おそらくこのような不都合から*2 pipsdist 形式のパッケージを一度手元に落としてきた後、それをビルドして bdist_egg とした後にインストールするという処理を行っていました。せっかくの egg が台無しです。

wheel

そんな状況がある一方で、wheelの開発がされていました。wheelはパッケージングツールであり、それで生成されるパッケージはwhl形式となっています。wheelがなぜ開発されたのか、その理由は筆者が本家サイトに書いてくれているので読んでみると面白いです。

読めばわかりますがwheeleggが抱えていた問題を解決するための仕様であるPEP-427やPEP-376を元に作られました。

No one has to rewrite setup.py for their own or the 20k+ other packages on PyPi unless a different build system does a better job.

Wheel が目指しているのは銀の弾丸ではなく、少しずつ既存の方法を解決していく地道な解決策であることが読み取れます。

何よりも大きいのはメジャーバージョンの違いも含めて、1つの whlでインストールされることです。そして、pipもバージョン7.0よりwhl形式に対応しています。このおかげで pip を利用したパッケージのインストールが高速になりましたし、Pythonのバージョン違いも神経質にならずに済みます。またバイナリインストールが可能なので、pipsdistからビルドする際にあったような、依存ライブラリがない、という状況も改善されました。これはPEP-427やPEP-376だけでなく、複数のLinuxディストリビューションでも動作するようにPEP-513で規定したことによります。(manylinux1

このような経緯でpipwheelを支える多くの仕様が標準で決められ、必要なものもいま議論されています。一つ大きなピースがあるとすれば sdist の仕様が実はPEPで決まっていないというところです。これも現在PEP-516、PEP-517で議論中、PEP-518はすでに策定済みとなっています。これが決まって setuptools による実装依存の仕様がなくなり、もろもろのPEPを実装しているPyPA管理下の distlib が対応していけば、いよいよ setuptools から解放されます。すべてPEPで定義された仕様を元にパッケージングができるようになり、これでPyPIにあるものがすべてWheelになれば皆が知らないうちに幸せになるわけです。

このサイトにあるように、すでに主要なライブラリのWheel化はかなり進んでいます。データサイエンス系のライブラリ(numpyscipyscikit-learnなど)もWheel化しています。Pythonしか使わないのであれば、データサイエンティストの皆様もwheelのおかげで特にインストールされている共有ライブラリなどを気にせずにパッケージをpipでインストールできるようになっています。pip の後ろで多くの人たちが議論を重ね、今あるものを壊れないように作り替えてきました。(pip のダウンロードが wheel のおかげで高速化されることを実装される前から知ってた人がどれくらいいましたか?意識しないでも壊れることなく動いていたのは彼らの努力のおかげです)

Anaconda

condaパッケージ

しかしながら、データサイエンティストの方々はまずAnaconda(conda)をインストールするという流れが最近あります。まあPython以外にも、そもそもランタイムが異なるRを使ってみたり、Scalaを使ってみたりすることもある人なら納得はできます。

しかしMinicondaならどうか?これならPythoncondaしか入れないのであれば別に virtualenvpip とできることは変わらないのではないか、と思いますがここでややこしい事情が出てきます。先の sdist のフォーマットです。たとえばここにあるAnacondaのパッケージを見てましょう。

たとえばJupyterの場合のパッケージはこれです。(Pythonのバージョンに応じたtarballがある)

これを展開してみるとこうなります。

% tree jupyter-conda
jupyter-conda
├── info
│   ├── files
│   ├── index.json
│   ├── meta
│   └── requires
└── lib
    └── python3.5
        └── site-packages
            ├── __pycache__
            │   └── jupyter.cpython-35.pyc
            ├── jupyter-1.0.0-py3.5.egg-info
            └── jupyter.py

見たことない形をしています。これは setuptools で吐かれる sdist や、wheelとは異なる形式です。*3ではいったい conda の形式はなんなんでしょうか。Anacondaの公式ドキュメントにパッケージのビルドについて書いてあります。

conda はパッケージインストーラであると同時にパッケージングツールでもあり、 conda ではまったく独自のパッケージング方法とホスティング方法を使ってパッケージを配布、インストールしているのです。これは現状 wheel互換性はありません

conda パッケージはPyPIにアップロードされているパッケージを元に作成することも、スクラッチから作ることもできますが、いずれにせよこれはContinuum Analytics社の独自仕様です。

なぜこんなことになっているのか

これは conda の開発履歴とPEPの策定時期を見てみると理由が見えてきそうです。まず conda の開発履歴をたどってみると、GitのログやChangelogから、condaのバージョン1.0が 2012年9-10月ころにはあったと書いてあります。

% git log --reverse --pretty="%h %cd - %s" | head -5
c9aea053 Mon Oct 15 17:14:59 2012 -0500 - first commit
acd8144f Mon Oct 15 17:16:45 2012 -0500 - add conda files
e40c59cb Mon Oct 15 17:17:29 2012 -0500 - add git ignore
967bb3a8 Mon Oct 15 17:29:01 2012 -0500 - since the already released version of conda is 1.0, this should be 1.1
b1120484 Mon Oct 15 17:47:43 2012 -0500 - remove pyc files

開発はもう少し前からされていたことを考えても、開発に着手したのは2012年前半と見てよいでしょう。一方で、wheelを標準とするためのPEPであるPEP-427が作られたのが2012年9月です。実際にドラフトとして採用されるまでの期間を考えるとそれより前には書き始めていたわけです。実はこの頃、Pythonのパッケージ管理方法の議論が活発でした。その様子は Python-Dev のメーリングリストを見てみるとよくわかります。

この頃の話を詳細にすると、またこれはこれで非常に長くなるのでもろもろ割愛して要点だけ記すと

  • Python 2.7でPython2系の終了が決まっていて、Language Moratorium後の初のバージョンPython 3.3が出る時期だったので、パッケージングについての議論が活発になっていた。(Python 3.3.0のリリースは2012年9月)
  • setuptoolsdistribute の対立が大きくなり、 distribute がメインストリームになりつつあった。(2013年に distributesetuptools にマージし、 setuptools はその後 pipvirtualenv も管理しているPyPAへ移管された。)

このような状況だったので conda の開発陣が業を煮やして自分たちでパッケージマネージャを作りたくなった気持ちもわからないではないです。実際、そのような気持ちだったであろうことは彼らの資料から見て取れます。*4

でもやっぱりcondaには歩み寄って欲しい

とはいえ、もう今は wheel がPEPで策定され標準パッケージング形式で決定していて、setuptoolspip でなんとなくでしか決まっていなかった sdist の仕様もきまりつつあり、すべて標準で決まってるんです。それだけじゃなくて、入れたパッケージをどう管理するか(誰がどういう理由でインストールしたかとか)とか、細かいパッケージの仕様まで長い年月をかけて標準で決めてきました。またパッケージの配布に関してもAPIがPEPで定義されています。これらは長い時間かけてPythonコミュニティが今動いているものをなるべく壊さないようにしつつ、コミュニティ全体がもっと便利にPythonを使えるように、と決めてられてきたものです。

このような状況を断片的にでもリアルタイムで見てきたので、いま conda がパッケージングに関してその流れを分断している状況は、個人的には悲しくもあり腹立たしくありという気持ちです。

conda が便利なのはわかりますが、勝手に独自パッケージを作って独自方式で配布するのではなく、パッケージを wheel 互換にしてくれて、PyPIにも whl 形式でパッケージをあげてもらえるような流れを作って欲しいと思います。実際に先にContinuum Analytics社のTravis氏のスライドで不満が漏らされていた numpy ですが、いまは whl 形式のパッケージが配布されているので、何の苦労もなく pip ですんなりインストールすることが出来ます。 conda の開発者が積極的にPEPの策定や wheel の作成と配布に関わってくれれば、コミュニティ全体でその恩恵にあずかれるので、そうなることを願うばかりです。*5

参考リンク

この記事にある細かな話は @aodag が過去のPyCon JPの発表で事細かに歴史的経緯含めて説明してくれているので、知りたい人はそちらもどうぞ。

PyPA(Python Packaging Authority)でパッケージング関連ツールの履歴を整理してくれています。

またPyPAで「現在推奨のパッケージツール」に関するドキュメントを常に更新し続けてくれています。迷ったらここを読みましょう。

最新のパッケージング動向を知りたいという人は各MLなどを追ってみましょう。

関連PEPとか

Appendix

  • Q1: buildoutについて触れられていませんが?
  • Q2: 開発環境の切り替えについて知りたいです。
  • Q3: 各OS(macOSWindowsLinuxディストリビューション等)でのパッケージ管理ツール経由でしかパッケージを入れたくありません
    • A3: その場合新しいバージョンのパッケージは登録されるまで待つしかないですが、それも一つの方法だと思います。いずれにせよそれらもwheelベースになるものと思われます。

コメントに関して

wheelのありがたさとAnacondaへの要望 - YAMAGUCHI::weblog

要望があるならcondaの開発者に直接伝えるべき (=>https://github.com/conda/conda/issues)。こんなとこで日本語で書いても1ミリも伝わらない。コミュニケーションについて非難している本人が一番コミュニケーションをとっていない。

2017/02/02 18:54
b.hatena.ne.jp

conda のパッケージのビルドに関するissuesはそこじゃなくて conda-build のレポジトリだし、記事公開時にすでにこの記事で書いたようなissueは上がってます。コミュニケーション云々いうなら、コメント欄開けてるので、記事コメントに直接書いてください。

*1:buildoutなどのZope関連のツールの話もあるのですが、これ以上書いていくととてつもない分量になるのでこの記事では書きません。

*2:pipでeggのインストールがサポートされなかった理由をかなり調べてみたけれど、決定的な理由が見つからなかった。

*3:比較したい人はこちらでどうぞ https://gist.github.com/ymotongpoo/7068beddbc7cf8a2015338c64425ca06

*4:ちなみにスライドの作者のTravis E. Oliphant氏はSciPyの作者

*5:自分はデータサイエンス系のこと以外にもPythonを使うので、そのためだけにcondaを入れたくはない。