2014/05/25 Sphinxメンテナ日記: 2to3やめてsixに切り換えました

Category: 'Python', 'Sphinx'

前置き: このエントリを読むと、既存のPython2実装の何かをPython3に移行する時の参考になるかもしれません。前置きおわり。

Sphinx-1.2のPython3対応

Sphinxはバージョン1.1からPython3対応していました。しかしSphinx-1.1と1.2はPython2で書かれていて、インストール時に 2to3 - Python 2 から 3 への自動コード変換 でコードを変換してPython3で動作させています。 しかし、2to3を使ったためにSphinxの機能以外のところでいくつかの問題が起きていました。

  • インストールに時間がかかる

    インストール時に2to3が実行されますが、多くの変換を行うため、対象ソースのコード行数に比例して時間がかかります。 Sphinx-1.2.2をローカルのソースからインストールした場合:

    • Python2.7: 7 秒
    • Python3.3: 41 秒
  • Python3のテスト実行に時間がかかる

    Sphinxはtoxでテストを行っていますが、テスト対象パッケージはテスト毎にvirtualenv環境に再インストールされるため、前述のインストール時間がテスト毎に2回(py32,py33)かかります。

  • Python3でのデバッグに手間がかかる

    Python3でのみ発生するバグの修正やPython3の新しいバージョンで動作しない場合、2to3変換後のコードに対して修正を行い、そのように変換されるように2to3変換前のリポジトリのコードを修正する必要があります。 うまく書けたか確認するために2to3を何度も実行するため、前述の変換コストがかかります。

もちろん、2to3自体はPython2が主流だったこれまでは、Python3にも楽に対応したい場合の道具としてとても役立つ物でしたが、Python3.4もリリースされ Python3が一般的になってきた 現状では、開発やインストールに時間がかかる2to3は良い選択とは言えなくなりました。

Sphinx-1.3のPython3対応

Sphinx-1.3は現在開発中ですが、既に2to3を使わない実装に切り替えがほぼ終了しており、Sphinx-1.3からは2to3による変換なしでPython3で動作します。

Sphinxはどのようにして2to3から脱却したか

チケット #1350 でいくつかの方法を検討しました。

これらのうち、Georgのコメントから、python-futureよりもsixを使う、出来ればsixも無しで実装する、という方針になり、最終的にはsixを使った実装を採用しました。 また、#1350のやりとりを通して、 Pygments もシングルソースでの実装に移行し、その結果、Pygments-2.0はPython-2.6, 2.7, Python-3.3+ で動作することになったようです (PygmentsのCHANGESより)。

ちなみに、python-futureは Comparing future.moves and six.moves の中で「six.movesの実装のためにDjangoのオートリロードや、py2exeやcx_freezeなどのツールで問題がある」という事を指摘しています。Sphinxもsixを採用したことにより、こういった問題に遭遇する可能性はあります。

Sphinxのシングルソース移行

移行は以下の3つのPull Requestで行われました(3つめは現在レビュー中です)。

  • Pull Request #208 python-futureの futurize --stage1 によるexcept文やprint文の変換
  • Pull Request #238 sixパッケージを採用し、Py2/3で移動したパッケージに対応、bytes/str/unicodeの調整、iterの調整
  • Pull Request #243 next()の調整、mapの調整、ur"" リテラルの調整、2to3の削除

3つめの Pull Request #243 では、部分的に from __future__ import unicode_literals を使っていますが、全体的には u"" リテラルを残してあります。 Sphinxで多く利用されている u"" リテラルを unicode_literals で書き換えるのは多くの手間と気を遣う作業のため、やりませんでした。 しかし、 u"" リテラルが復活したPython-3.3以降でも ur"" リテラルは使用できないため、その部分についてだけunicode_literalsで対処してあります。

ところで、Python3.2では u"" リテラルが使用できません。このため、Sphinx-1.3もPython-3.2では動作しなくなる予定です。

なお、Sphinx-1.2が依存している Pygments (Georg作)と snowballstemmer (shibu作)もPython-3.2では動作しません。今後はPython-3.2対応はどんどん厳しくなっていきそうですね。

インストール時間の改善

Sphinx-1.3のソースからのインストール時間を計ってみました:

  • Python2.7: 6 秒
  • Python3.3: 6 秒

ところで最近のSphinxはwheelパッケージもリリースしているので、pip-1.5以降を使っていればwheelパッケージでインストールされるので、インストール時間はもっと短かったりします。 wheelがどのくらい速いかについては@aodagによる pipとwheelでテスト環境構築をスピードアップ が詳しいです。 Sphinxのテストにwheelを活用したらめちゃ速くなりました( https://drone.io/bitbucket.org/shimizukawa/sphinx-py3-native/18 )。@aodag、良い記事をありがとうー。

まとめ

  • そろそろ 2to3 やめよう
  • python-future のfuturizeは便利
  • sixpython-future どちらが良いか誰か調査して
  • u"" リテラルめんどくさい
  • Python-3.2 はDropしよう
  • wheel速いよ、開発に活用すると良いよ