今年はPython3.3!
そろそろ乗り換えモードじゃない?


      BPStudy #54
     小田切 "aodag" 篤
お前誰よ?

小田切篤
@aodag

とある代々木の会社のエンジニア


http://facebook.com/aodag
http://aodag.posterous.com
アジェンダ

なぜPython3?
Python アドベントカレンダー2011 (Python3)
Python3対応奮闘記
Python3対応に役立つツール
Python3対応の勘所
Python3って?

Pythonのバージョン3
後方互換性のない変更を含む

Python2.6 Python3.1
Python2.7 Python3.2
と並行してリリースされてきた

今年の3月にPython3.3a1のリリースが予定されている
なぜPython3

Python2系は2.7で終わり
もうPythonへの機能追加はPython3でしか行われない

Python3は単なるメジャーバージョンアップ

Python3でうれしい?
いつかはPython3になってしまう。
Python3対応は必然。
Python3対応に手を出すわけ

去年の夏にフォームライブラリを調べた(PyConJP 2011で発表)した
が、Python3に対応したものがまったくないことが気になっていた
Python3対応に手を出すワケ

今ならPython3対応することで、オープンソースコミュニティに貢献し
やすい。(大半が機械的な作業なので、実はローカライズについで手
が出しやすい)
Python3対応奮闘記

ある日、ある雪山の開発合宿、あるIRCで

aodag: Python3やってるひとー。フォームライブラリ何使ってる?
other: Python3対応してるフォームライブラリないよ
mcdonc_: deformとcolander対応する予定だけど、優先度高くない
よ。

※ mcdonc_はpyramidの主要開発者です
aodag: deformとかなんで2.4とかまだサポートしてるのん?

mcdonc_: ちゃんとした理由はないよ。3対応するなら2.6以降だけの
サポートでいいね。

aodag: pyramidと同じようにワンソースアプローチだといいね。

mcdonc: それでうまくやってけそうだからね。
mcdonc_: Start Hacking ;)
(え、俺がやるの....?)
ということで

pylonsprojectとrepoze、Paste の以下のライブラリについてpy3対応

 ● peppercorn リリース済
 ● colander リリース済 
 ● deform リリース済
 ● repoze.who 作業中

やろうかなと思うもの
 ● pyramid_who
 ● pyramid_rpc
 ● PasteScript
現在Python3対応しているライブラリ

http://pypi.python.org/pypi?:action=browse&c=533&show=all

SQLAlchemy
lxml
PyQt
bottle
pyramid
Sphinx
numpy/scipy
ipython
...
Python3対応のアプローチ(1)
ワンソースアプローチ
      2to3変換や
バージョンごとのモジュール切り替え
     を行いません
Python3対応のアプローチ(1)
ワンソースアプローチ
Pylons、repozeはこの方法

2系は2.6以降のサポートにします
2.5以前は文法の差異を吸収できません
toxにより、対応バージョンのテストを一度に実行できるようにします


         3系は3.0,3.1は
       無視してもかまいません。
今現在
Python3に手を出すような人は
    3.2に移行してます。
Python3対応のアプローチ(2)
2to3を使う方法
2to3を使ってソースを変換する

SQLAlchemyはこの方法

setupで、use_2to3=Trueにすれば、インストール時に2to3でコード変
換してくれます
2to3

変換後にちゃんと動くかは、やはりテストが必要。
開発中に2to3すると、ソースを直接書き換えられてしまう。
toxで2to3変換後のソースをテストするにはコツが必要。
Fuckin' 2to3 !


2to3は使いたくない!
使いたくないワケ

テストコードも2to3するの?
変換したテストコードで変換後のテスト...怖い><

ws_comma_fixer が重い
Python3対応の準備 ツールをそろえる

Python実行系の各種バージョンをそろえる

対応させたいバージョンはすべてそろえましょう
Python3対応の準備 ツールをそろえる

tox
virtualenv
nose
pytest
distribute
coverage
tox - 対象バージョン全テスト

[tox]
envlist = py26,py27,py32,pypy

[testenv]
deps =
    nose
commands =
    nosetests []
SIX - 汎用互換ライブラリ

pylonsproject内では個々のパッケージでcompat.pyを作ってますが、
汎用的なものをまとめたライブラリにsixがあります。
すでに誰かやってないか聞く

もしかしたら、もう誰かやってるかもしれない
ML, IRC, Twitter などで聞く
多分いないので、
聞いてしまったことにより、
自分がやることになります。
対応バージョン範囲を確認する。
python2.5以前を切り捨てるかどうかがポイント
切り捨てられないなら2to3を使うか、ダーティハックです。
Start Hacking ;)
手順(?) ソースを取り寄せる

pylonsprojectはgithubにリポジトリがあるのでとても楽でした。
githubマジ神

ブランチを切ります

git checkout -b py3k

プロジェクトのやり方にあわせましょう。
テスト環境作成

ひとまず無難なところで2.6か2.7で作業環境を作ります。

mkvirtualenv -p python2.7 testenv

テストツールインストール
pip install tox nose coverage

依存ライブラリなどインストール
pip install -e .
手順(?) 既存のテストを確認する

       そもそも
   2系で動くテストなければ
     はじまりません。

      なければ
テストを充実させるところからです。
既存のテストを確認する
カバレージの確認

コマンドラインオプション

nosetests --with-coverage --cover-package=テスト対象
既存のテストを確認する 
noseの設定
setup.cfg でコマンドラインオプション固定


[nosetests]
with-coverage = 1
cover-package = テスト対象
toxを設定する

;; tox.ini

[tox]
envlist = py26,py27,py32,pypy

[testenv]
deps =
   nose
   coverage
commands =
   nosetests []
toxを動かす

コマンド自体は簡単です

$ tox

1回目は対象バージョンすべてのvirtualenvを実行するので時間がか
かります。
多分python3で動かないことを
  確認できるでしょう。 
依存ライブラリを確認する

py3k対応していないライブラリをどうするか?

● 依存からはずして代替ライブラリに変更
● 依存からはずして、同等機能をついか
● そのライブラリをpy3k対応する
   ○ 本当に対応する
   ○ コードをとりこむ
● 諦める
互換モジュールを追加する

● 独自にcompatモジュールを作る
● sixを使う
compatモジュール

バージョンフラグ
py3 = sys.version_info > (3,0)
compatモジュール

名前の変わった標準ライブラリ

StringIO
urllib周り
cookieやconfigparser, httplib
compatモジュール

str, bytes の差異を吸収するユーティリティ

binary_types
text_types

text_
bytes_

などをpython2用とpython3用にそれぞれていぎ
文法の非互換に立ち向かう

2大文法エラー


● 例外ブロック
● ユニコードリテラル
例外ブロック

old
except Exception, e:


new 2.6から使える
except Exception as e:
ユニコードリテラル

ユニコード文字列リテラル
u"日本語"


compatユーティリティでラップする

text_("日本語")
importエラーをなおす

よくあるimportエラー

 ● 相対import
 ● 名前が変わった標準ライブラリ
名前が変わったものなどcompat内で解決
import先をcompat経由に変更する
相対import

2系では相対import優先。3系では絶対import優先


import amodule

どちらでも相対import優先

from . import amodule
コード、テストを修正する

ここまでくればいつもどおり!
いきなり80%のテストが失敗しますが、
心折れずに進めましょう。
Python3対応の勘所(1) iterator

組み込み関数がのきなみiteratorに対応
zipもfilterもrangeもlistではなくiteratorを返す


それぞれのiterator版だった、izipやifilter, xrangeなどは消滅
で す が 
len(zip((1,2,3), ('a', 'b', 'c')))

3系だとNG!
やりたければlistコンストラクタを明示的に呼び出す

len(list(zip((1,2,3), ('a', 'b', 'c'))))
Python3対応の勘所(2) 
strとbytes
Python3の文字列はユニコード
Python2のバイト配列に対応するものはbytesに変更

Python2でなんとなく使ってたバイト文字列とユニコード文字列の違
いを意識する場面が増えた
多くの関数、ライブラリがstr前提に

json.loads strしか受け取れない
json.load strを返すファイルライクオブジェクトじゃないとだめ

open関数が返すオブジェクトはstrを返すファイルライクオブジェクト
StringIOとBytesIO

ioモジュールに統合
bytesの注意点

bytesとstrは結合できない

NG b'a' + 'a'

2系では自動変換されてました
bytesの注意点

%演算子を使ったフォーマットが使えない

NG b'%d' % 10
bytesの注意点

コンストラクタ

3のbytes
bytes(source, encoding, errors)

2のころのstr
str(object)
bytesの注意点

2のころ

>>> str(b'a')
'a'

3だと

>>> str(b'a')
"b'a'"
要するに

       3系のbytesは
        2系のstrと
       互換ではない
Python3対応の勘所(3) 
strがちゃんとしたiteratorを実装している
__next__や__iter__が実装されiteratorとして正しいインターフェイスと
なった。

し か し
2系で使われていたバッドノウハウ
__iter__ の有無でlistとstrを振り分ける
は、もう使えません。ちゃんとisinstanceを使いましょう。
Python3対応の勘所(4)
basestringがなくなった
2系のstrとunicode共通のスーパークラスだったbasestringがなくなり
ました。

isisntance(s, basestring) はできません

バージョンチェックで str_typesを作って対応
2系のstr_types
str_types = (str,unicode)
3系のstr_types
str_types = (str,)
Python3対応の勘所(5)
相対importの罠
2系の場合と3系の場合で優先順位が違う

2系 相対importが優先
3系 絶対importが優先

a/__init__.py
a/a.py

3系で a/__init__.py で import aをやると自分をimportする!
この場合は、from a import a か from . import a としましょう。
相対importの罠にはまったライブラリ

iso8601


最終リリース(2007年)からissuesも放置状態...
作者への連絡方法も分からず、
py3k対応のスタンスも確認できない

MITライセンスだったので、ソース取り込みで対応
例外文法

とくにキャッチする部分

2系のみ
except Exception, e:

2.6以降と3系
except Exception as e:
どのバージョンでも動く? 2.4や2.5でも3.2でも動く。
except Exception:
  e = sys.exc_info()[1]

多分一番ポータブル!でも、なんかいや
まとめ

鶏が先か卵が先か。せっかくなので提供する側になりたいです。

テストがすでにそろってる状態であれば、Python3対応は機械的に
進められます。テスト大事。

当然Python3での変更を調べることになります。Python3を仕事で使う
前にオープンソースプロジェクトに貢献しつつ、はまりどころを事前に
知ることができます。しかも、世界レベルの開発者からのアドバイスま
でもらえます。
http://www.voidspace.org.uk/python/articles/porting-mock-to-python-
3.shtml

http://pypi.python.org/pypi/six

BPStudy#54 そろそろPython3