CUBE SUGAR CONTAINER

技術系のこと書きます。

Python: 古いバージョンを使い続けるということ

例えば標準出力にユニコード文字列を書き込むという単純極まりない処理をやってみる。

Python 2.7 を使うとこんなかんじ。

$ python --version
Python 2.7.10
$ python
>>> import sys
>>> sys.stdout.write(u'うにこーど\n')
うにこーど

何の問題もない。

Python 3.5 ならこう。

$ python --version
Python 3.5.1
$ python
>>> import sys
>>> sys.stdout.write(u'うにこーど\n')
うにこーど
6

write() メソッドが書き込んだ文字数を返すようになってる。

さて、肝心の Python 2.6 を使うとこうなる。

$ python --version
Python 2.6.9
>>> import sys
>>> sys.stdout.write(u'うにこーど\n')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128)

何か例外出ましたけど。

原因を調べたら古式ゆかしい Python のバグらしい。 Python 2.6 のサポートは既に終わっているので、この不具合が直ることはない。

stackoverflow.com

一応、解決策がないわけではなくて次のようにすると回避できる。

>>> import sys
>>> import imp
>>> imp.reload(sys)
<module 'sys' (built-in)>
>>> import locale
>>> sys.setdefaultencoding(locale.getpreferredencoding())
>>> sys.stdout.write(u'うにこーど\n')
うにこーど

あるいはファイルライクオブジェクトをラップするやり方もありかな。

>>> import locale
>>> class StdWrapper(object):
...     def __init__(self, stream):
...         self.stream = stream
...     def __getattr__(self, name):
...         return getattr(self.stream, name)
...     def write(self, text):
...         # unicode は 2.x だけにあるシンボルなので注意
...         if isinstance(text, unicode):
...             text = text.encode(locale.getpreferredencoding())
...         return self.stream.write(text)
... 
>>> import sys
>>> wrapper = StdWrapper(sys.stdout)
>>> sys.stdout = wrapper
>>> sys.stdout.write(u'うにこーど\n')
うにこーど

ただ、どちらも LANG=C な環境なんかだと上手くいかないみたいだ。

結論

古いバージョンを使い続けることはリスク。