レガシーコードって朔というP2P掲示板「新月」の実装のことなのですけどね。なにしろ2005年に作り始めてて、その頃はGUIアプリのMVCの概念は知ってたけど、webアプリのMVCの概念は知らなかったので、普通にコントローラ内のprint文でHTMLの出力をしてたりとか、そういうやつです。まあPython2.7のサポートも2020年まで伸びたし、そんなに急ぐこともないというのもあるのですけど、まあやってみるかなという感じで対応しました。
- Python2で -3 オプションをつけて警告を出して、警告がなくなるまで修正。ユニットテストがあれば全部警告が出せるんですけど、動かしてみて警告がなくなるまでという感じだったような。それとも構文解析した時点で警告が出るんだったかな、よく覚えてないです。ただ何回かに分けてコミットされてるので、警告が出る度に直してたんじゃないかな。
- テンプレートエンジンとしてCheetahを使ってたのですが、まだPython3に対応してないので、Jinja2に変更しました。なおCheetahはYelpがフォークして盛んにコミットしてるので、社内で使ってるんじゃないですかね。
- 2to3をかけます。これで自動的にPython3対応のコードが生成されます。あとはエラーが出るところを探して修正していくだけです。ユニットテストがあれば…
- http.server.CGIHTTPRequestHandler を継承して、forkしてるところをマルチスレッドに書き換えてるのですが、親クラスが変わったのでその変更点を取り込みます。こういう継承の使い方はよくないです。
- ファイルを開くときに文字コードを指定して、ユニコード文字列で開くようにします。これもけっこう辛い戦いでした。
- クライアントからHTTPで受け取った文字列や、サーバー間通信で取得した文字列も、同じようにユニコード文字列にする必要があります。アスキーのfooとユニコードのfooは == で比較すると別物なので、なぜか通信がうまくいかないということになってちょっと気がつくのに時間がかかりました。
- MD5のハッシュを作るとか、base64の変換をするとかのところも、ユニコード文字列なのかバイト列なのかに注意が必要です。
- バイト列とユニコード文字列の足し算もうっかりやっちゃいます。そこを通るとエラーになる系ですね。
- バイナリを使うときはStringIOがBytesIOに変わるとか、まあ言われてみればその通りなんですけどね。
- iter(foo).next() → next(iter(res)) とか、これは2to3が見落してたんですかね。
- サムネイルを作るのにPILという画像処理ライブラリを使ってたのですが、Python3だと(とりあえずは?)Pollowの方がよいらしくて、ライブラリ群はaptitudeでインストールする方針だったのですが、止むを得ずpipでインストールすることにしました。
- LANG=ja_JP.UTF-8とかの環境変数で、os.environへのアクセスが変わってくるという問題があって、これもけっこう辛かったですね。最終的にはos.environを普通の辞書にコピーすることで解決しましたが、見た目は辞書なのに中身は違うというのはけっこう罠です。このときは数日間公式ゲートウェイが落ちてたのですが、普段から403/404エラーがたくさん出てたので、携帯からlogwatchのメールを見ても見逃がしてたという恥ずかしい話が。