はじめに
こんにちは、Python界のラファエル・ナダルです。全豪オープンテニス、盛り上がりましたね。さて、先日次のようなエントリーを立て続けに書いたんですが、「なぜAnacondaに関しての記述がないのか」という突っ込みをもらったので、参照用にメモを残しておきます。
- Pythonの仮想環境構築 2017.01版 - YAMAGUCHI::weblog
- Pythonの環境設定でむかついてる人はとりあえずこれをコピペで実行してください 2017.01 - YAMAGUCHI::weblog
なおこの記事の作成にあたっては @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
- eg)
- パッケージインストーラ : PyPIなどで配布されているモジュールをインストールするためのツール。通常はこちらを使うことばかり。裏側で何をしているか意識することは少ないです。
- eg)
easy_install
,pip
- eg)
現在は setuptools
, wheel
, pip
は virutualenv
とともに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
や先の setuptools
が setup.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 pip
は sdist
形式のパッケージを一度手元に落としてきた後、それをビルドして bdist_egg
とした後にインストールするという処理を行っていました。せっかくの egg
が台無しです。
wheel
そんな状況がある一方で、wheel
の開発がされていました。wheel
はパッケージングツールであり、それで生成されるパッケージはwhl
形式となっています。wheel
がなぜ開発されたのか、その理由は筆者が本家サイトに書いてくれているので読んでみると面白いです。
読めばわかりますがwheel
はegg
が抱えていた問題を解決するための仕様である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のバージョン違いも神経質にならずに済みます。またバイナリインストールが可能なので、pip
でsdist
からビルドする際にあったような、依存ライブラリがない、という状況も改善されました。これはPEP-427やPEP-376だけでなく、複数のLinuxディストリビューションでも動作するようにPEP-513で規定したことによります。(manylinux1
)
このような経緯でpip
とwheel
を支える多くの仕様が標準で決められ、必要なものもいま議論されています。一つ大きなピースがあるとすれば sdist
の仕様が実はPEPで決まっていないというところです。これも現在PEP-516、PEP-517で議論中、PEP-518はすでに策定済みとなっています。これが決まって setuptools
による実装依存の仕様がなくなり、もろもろのPEPを実装しているPyPA管理下の distlib
が対応していけば、いよいよ setuptools
から解放されます。すべてPEPで定義された仕様を元にパッケージングができるようになり、これでPyPIにあるものがすべてWheelになれば皆が知らないうちに幸せになるわけです。
このサイトにあるように、すでに主要なライブラリのWheel化はかなり進んでいます。データサイエンス系のライブラリ(numpy
、scipy
、scikit-learn
など)もWheel化しています。Pythonしか使わないのであれば、データサイエンティストの皆様もwheel
のおかげで特にインストールされている共有ライブラリなどを気にせずにパッケージをpip
でインストールできるようになっています。pip
の後ろで多くの人たちが議論を重ね、今あるものを壊れないように作り替えてきました。(pip
のダウンロードが wheel
のおかげで高速化されることを実装される前から知ってた人がどれくらいいましたか?意識しないでも壊れることなく動いていたのは彼らの努力のおかげです)
Anaconda
condaパッケージ
しかしながら、データサイエンティストの方々はまずAnaconda(conda
)をインストールするという流れが最近あります。まあPython以外にも、そもそもランタイムが異なるRを使ってみたり、Scalaを使ってみたりすることもある人なら納得はできます。
しかしMinicondaならどうか?これならPythonとconda
しか入れないのであれば別に virtualenv
と pip
とできることは変わらないのではないか、と思いますがここでややこしい事情が出てきます。先の 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月)
setuptools
とdistribute
の対立が大きくなり、distribute
がメインストリームになりつつあった。(2013年にdistribute
がsetuptools
にマージし、setuptools
はその後pip
やvirtualenv
も管理しているPyPAへ移管された。)
このような状況だったので conda
の開発陣が業を煮やして自分たちでパッケージマネージャを作りたくなった気持ちもわからないではないです。実際、そのような気持ちだったであろうことは彼らの資料から見て取れます。*4
- Packaging and Deployment with Conda // Speaker Deck
- Python Packages and Environments with conda | Continuum
でもやっぱりcondaには歩み寄って欲しい
とはいえ、もう今は wheel
がPEPで策定され標準パッケージング形式で決定していて、setuptools
と pip
でなんとなくでしか決まっていなかった sdist
の仕様もきまりつつあり、すべて標準で決まってるんです。それだけじゃなくて、入れたパッケージをどう管理するか(誰がどういう理由でインストールしたかとか)とか、細かいパッケージの仕様まで長い年月をかけて標準で決めてきました。またパッケージの配布に関してもAPIがPEPで定義されています。これらは長い時間かけてPythonコミュニティが今動いているものをなるべく壊さないようにしつつ、コミュニティ全体がもっと便利にPythonを使えるように、と決めてられてきたものです。
このような状況を断片的にでもリアルタイムで見てきたので、いま conda
がパッケージングに関してその流れを分断している状況は、個人的には悲しくもあり腹立たしくありという気持ちです。
conda
が便利なのはわかりますが、勝手に独自パッケージを作って独自方式で配布するのではなく、パッケージを wheel
互換にしてくれて、PyPIにも whl
形式でパッケージをあげてもらえるような流れを作って欲しいと思います。実際に先にContinuum Analytics社のTravis氏のスライドで不満が漏らされていた numpy
ですが、いまは whl
形式のパッケージが配布されているので、何の苦労もなく pip
ですんなりインストールすることが出来ます。 conda
の開発者が積極的にPEPの策定や wheel
の作成と配布に関わってくれれば、コミュニティ全体でその恩恵にあずかれるので、そうなることを願うばかりです。*5
参考リンク
この記事にある細かな話は @aodag が過去のPyCon JPの発表で事細かに歴史的経緯含めて説明してくれているので、知りたい人はそちらもどうぞ。
- aodagの資料
- PyCon JP 2016 パッケージングを支える技術 pyconjp2016(発表動画)
- PyCon JP 2015 発表動画
- PyCon JP 2014 パッケージングの今(発表動画)
- PyCon JP 2013 パッケージングの今と未来(発表動画)
PyPA(Python Packaging Authority)でパッケージング関連ツールの履歴を整理してくれています。
またPyPAで「現在推奨のパッケージツール」に関するドキュメントを常に更新し続けてくれています。迷ったらここを読みましょう。
最新のパッケージング動向を知りたいという人は各MLなどを追ってみましょう。
関連PEPとか
pip
を標準と一緒に配布することを決めた- 誰がどういう経緯で現存のパッケージをインストールしたのかの情報を持つための仕様
- パッケージのバージョニングに関する仕様
- パッケージのメタデータ(PKG-INFO)に関する仕様
- whl形式に関する仕様
- パッケージのファイル名等に入るタグに関する仕様
- 複数のLinuxディストリビューションで動作するwheelの条件とそのタグに関する仕様
- 複数のPythonパッケージのビルドシステムに中立な設定やディレクトリ構成を決める仕様
sdist
をビルドするために必要なパッケージングツールを記述するための仕様- PyPIで使われていないファイルを消す提案
- パッケージレポジトリのAPIを決める提案
- Language Moratoriumの宣言
- 本記事に関連するPEPを実装している
distlib
パッケージ。pip
も内部で利用している。
Appendix
- Q1: buildoutについて触れられていませんが?
- Q2: 開発環境の切り替えについて知りたいです。
- A2: virtualenv (venv) はPEP-405で標準で利用されることになりました。(参考: http://ymotongpoo.hatenablog.com/entry/2017/01/29/002039)
- Q3: 各OS(macOS、Windows、Linuxディストリビューション等)でのパッケージ管理ツール経由でしかパッケージを入れたくありません
- A3: その場合新しいバージョンのパッケージは登録されるまで待つしかないですが、それも一つの方法だと思います。いずれにせよそれらもwheelベースになるものと思われます。
コメントに関して
wheelのありがたさとAnacondaへの要望 - YAMAGUCHI::weblogb.hatena.ne.jp要望があるならcondaの開発者に直接伝えるべき (=>https://github.com/conda/conda/issues)。こんなとこで日本語で書いても1ミリも伝わらない。コミュニケーションについて非難している本人が一番コミュニケーションをとっていない。
2017/02/02 18:54
conda
のパッケージのビルドに関するissuesはそこじゃなくて conda-build
のレポジトリだし、記事公開時にすでにこの記事で書いたようなissueは上がってます。コミュニケーション云々いうなら、コメント欄開けてるので、記事コメントに直接書いてください。