ワタシのサイトにおける Python 2.7 の今後の扱い

本日は 2019年3月7日。

あと9ヶ月で Python 2.7 は「公式に」死ぬ:


で、ワタシのサイトで今後 Python 2.7 についてどうしていくか、そろそろちゃんと表明しとかないといけないと思って。

Pythonのどのバージョンを採用すべきか? みたいな今更な話」で触れたことだけれど、「事情持ち」てのは不可避なことであって、ワタシにも特殊な事情がある。

今はもうそのプロジェクトからは離れてしまったけれど、こんなプロジェクトにいた:

  1. 設計が 2010 年くらいから 2014 年くらいまで
  2. 製作が 2015 年
  3. 運用試験が 2016年丸々
  4. 実稼動が 2017年から8年間

全体は C++ 中心のプロジェクトなのだけれど、一部 Python を導入した。けれども「Python の検討」において、まさに不幸な時期に当たっているわけね。まさに 2010年付近なんてのは、まだまだ「Python 2.x にしか対応しない OSS」も世に溢れていた。だから「その時点ではやむなく 2.7」しか選択肢はなかったわけ。「Pythonのどのバージョンを採用すべきか? みたいな今更な話」でも言ったように、「石橋を叩いて叩いて叩いて叩いて叩きまくるホワイトカラー指向な CentOS を採用し、なおかつ「枯れてる古いものほど良いものだ」な指向」に運よくハマれば「Python 2.6 以外まかりならん」になってしまうわけで、「2.7 の導入ですら難儀」だった。実際それで喧嘩した、ワタシは。

ということがあるので、まぁ離れてしまったプロジェクトについてワタシが気に病むこともないんだろうなぁ、と思うこともないでもないのだけれど、気分的にはやっぱり彼らのこともほっとけないわけだ。なので、少なくともワタシのサイトでは2025年までは 2.7 を見捨てない


さて、問題はここから。じゃぁ「あなたはどうすべきなのか?」。

一つには、まずワタシは「Python 2.7 でも 3.4+ でも動作するスクリプト」の形で紹介し続けることになるれど、真似しないでくれていいし、是非とも真似しないで欲しい。ワタシは Perl 4 から Perl 5 に移行しきるまでにどれだけの時間がかかったのか良く知っている。おそらく Python 2.7 も、それに近い運命を辿る。最低でも5年、最悪の場合は 10年延命するかもしれない。だからといって「それでいい」と思って欲しくない。率先して 3.4+ を使うべきだし、人にもそう勧めるべき。

「公式に死ぬ」ということは、セキュリティバグすら FIX されないということ。OSS も喜んでサポートを打ち切り出すはず。そしてである、もう 4.0 の話も出てるんだぞ。

全部の決断の自由があるなら、「積極的に Python 2.7 を見捨てる」ようにして欲しい。そういう自由がない場合でも、「Python 3.x で書かれたもの」を保守するのなら、そしてそれが許されるのであれば、少しずつでもいいから「Python 2.7 向け」部分を積極的に破棄していって欲しい。(おそらく「許される」タイミングは、システム刷新で正規の時間内でテストし直せる時。)

そういう自由がない場合で、「Python 2.7 で書かれたもの」を保守しなければならない場合は、少しずつでもいいので「Python 3.4+ でも動作する」を検討し始めて欲しい。これはとても大変だけれど、unittest, doctest がきちんと丁寧に書かれてるなら、なんとかなるはず。そうでないなら、まずは unittest, doctest をちゃんと書くことから始めてください。


2019-03-10追記:
あまりに放置プレイ過ぎるかなと思うので、少しだけ補足。

まず、Python 2.x コードを保守している場合に、「Road to Python 3 の第ゼロ歩」について。これは以下に尽きる:

  1. VCS を利用していなかったなら、今からでも使う。必須。
  2. テストカバレッジを計測「出来るようにしておく」。

VCS も使わずに Python 3 対応なんてことが許されるのはせいぜい「20行以内で書かれたスクリプト」くらい。それなりのものなら、そして「プロジェクト・チームとしての採用がおおごと」だとしても、「オレだけは」導入しなさい。

カバレッジはものによっては結構難儀かもしれないが、頑張るべき。そして、「カバレッジ100(or に限りなく近い)」でない場合、「とにかくまずはテストを書く」ことが不可欠、ということになるのだが、開発のどのフェーズにいるかによっては、すぐに始められるかどうかは場合によるだろう。つまりこれは「第ゼロ歩」の範疇を超える。

で、次に何を考えるべきかは、これは、「Python 2.x を捨てるのかそうでないのか」。

ここで「捨てる」場合は、「2to3.py」を適用しても良い。無論テストが万全ならば、という大前提。2to3.py は完全ではないし、「テストし直さなくて良い」なんてことには万が一にもならない。規模がそれなりのものなら、テストは大々的に壊れる。結構長い道のりにはなるだろう。けれども「Python 2.7 を維持する」よりは遥かに楽なはず。

で、「Python 2.7 を捨てない」場合、もしくは、「段階的に移行したいのですぐには Python 2.7 を捨てない」場合は、真っ先には「まずは Python 2.6 以下を捨てる」ことから始めること。これも同じくテストが万全であることが前提。

残念なことだけれど、「ほとんど真剣なテストし直しを要さずに出来る Python 3 向け対応」は非常に限られている。けれども「ないわけではない」。少なくともワタシが真っ先に思いつくのは「print」の件と、「絶対インポート」の件の2つ。ともに「2to3.py が面倒みてくれる」対象であるが、当然それをすると「Python 2.7 で動作しなくなる」。「Python 2.7 でも動作するまま Python 3 対応」するなら、「from __future__ import xxx」を挿入する必要がある。こんなんでも結構な手間ではあるので、簡易なスクリプトを書いてみた:

Unix 的シェルなので、Windows ユーザの場合は MSYS や cygwin を使っておくれ。でこんな具合に使う:

1 [me@host: ~]$ sh _2to3=/path/to/2to3.py ./pre2to3_step_zero.sh mypkg/hoge.py 

2to3.py スクリプトは本来ディレクトリ丸ごとの処理が可能だが、あえてそのことを無視している。一個一個確実にやった方がええぞ、こんなん。スクリプトが行った変更は、一つ一つ diff ツールで確認すること。

一応ワタシのスクリプトが問題を起こさない限りは、「テストし直さなくてもまず 99% 問題を起こさない」はず。そしてこんな恵まれた「おおごと」はこれしかない。

なんつーか、「print の問題」って、知らない人からは一番目立つしとてつもなく大変なように見えるんだけれど、実際はこれほど簡単に措置出来るものは他にはないんだわ。プロジェクトによっては「printだらけ」なんてのはあると思う。その場合、「なんだかんだとてつもなく変更箇所が多い」なんてこともあるはず。つまり「Python 3 向け対応としてはかなりのノイズ」。ゆえ、とっとと済ませてしまうのは悪いことじゃない。

紹介したスクリプトと同じようなノリで変更することが可能な「fixes」(Lib/lib2to3/fixes 内にある)はないではないのだが、「テストし直しを要しない」ものはない。buffer の件などが「同じようなノリで変更可能」な一つだが、これも要するに「文字列 = UNICODE」化の一環なので、ほとんどの場合振る舞いを壊す。いずれにしても fix_print, fix_import 以外はどれも「覚悟が必要」。

というわけで、ここまでが「第ゼロ歩」だとして、「何が第一歩のうちのはじめの一歩か?」。

個人的な意見としては、以下から考えると良いと思っている:

  1. 「ほんとに print?」
  2. doctest の改善

前者は、「せっかく print を書き換えたのに…」になりかねないが、つまりは「ロガーの範疇なんじゃないのか?」について、思いを馳せるべき、て話ね。

後者が問題で、「Python 2 から 3 での最も根本的な変更2つ」にモロに影響を受けてしまうのよ、doctest って。そう、「long」と「unicode」の2つね。もしも doctest をこんなふうに書いてるなら要注意:

1 def fun(x):
2     """
3     >>> fun(3000)
4     27000000000L
5     """
6     return x**3

これは python 3.x で動かすとテストが壊れる。long, unicode だけでなく、とにかく「データの表現」に関わるものは、軒並みテストが壊れるはず。ので、doctest を、明示的に print するように書き換えておくと良い:

1 def fun(x):
2     """
3     >>> print("%d" % fun(3000))
4     27000000000
5     """
6     return x**3

unicode についてもわかるよね。「u」プレフィクスが問題を起こす。リストの中身なんかは結構大変ではあるんだけれど、map やら filter を駆使すれば多分なんとかなるはず。

ただ、doctest というか docstring というのは本来の目的は「ドキュメンテーション」なのだから、この「print」のためのゴテゴテが「あまりにゴテゴテ」になると「ドキュメント」として機能しなくなることに注意。なのでそういう場合は「あまり doctest だけでやろうとせずに」適宜 unittest に移動していくこと。

ここまでが概ね出来たら、ようやく「本格的な Python 3 向け対応」を「始められる」。インターフェイスの移動に関係するものなら、six を使うのも良いだろう。unicode まわりについては、まずは「from __future__ import unicode_literals」から始めると良いと思う。これ一個入れるだけでも物によっては「かなりのおおごと」になるけれど。

 

とりあえず「放置プレイ過ぎたので補足」のレベルで言うべきなのはこのくらい。以後のそれぞれについては、もうね、「地道にやるしかない」と思うんだよね。そういった話もいずれ書きたくなるかもしれない。書きたくなれば書くかも。



Related Posts