2012/06/23

Pythonスクリプトの出力をリダイレクトしたらエラーになった

このブログサイトに登録してあるキーワードを出力するスクリプトをgdataライブラリを使って、pythonで書いているのですが、出力結果をファイルにキャッシュしておこうとしてラップしているbashスクリプトに../list_labels.py | tee labels.txt を追加したところ、次のようなエラーが発生しました。

  File ".../python/list_labels.py", line 133, in run
    self.list_labels()
  File ".../python/list_labels.py", line 125, in list_labels
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-7: ordinal not in range(128)

問題になったのは次のようなコードの中で、print labelをしているところです。

問題になった該当コード周辺

...
    l = []
    for label in set(labels):
      l.append(label)
      pass
    for label in sorted(l):
      print label
      pass
    pass

Googleでとりあえず検索してみた結果

いくつかの方法がありましたが、sys.setdefaultencodingにencodingを記述するといった方法がありました。

import sysをしてからdir(sys)でみると、少なくとも手元のpython 2.7.3にはそんなメソッドは定義されていません。

次にヒットしたのはunicodeに文字列を変換するものですが、どちらかというとスクリプト中に埋め込んだutf-8文字列を画面に出力するための方法を解説しているもので、リダイレクトの時に問題が発生している事については触れられていませんでした。

pythonのバージョンに依るのかもしれませんが、この他の方法もいまいち決定的なものはありませんでした。

unicodeに変換するのではなく、リダイレクトの時の正しいencodeを指定する

解決策は次のようにencode()関数を追加して'utf-8'に変換しました。

変更後の該当コード個所の抜粋

    l = []
    for label in set(labels):
      l.append(label)
      pass
    for label in sorted(l):
      print label.encode('utf-8')
      pass
    pass

リダイレクトの時にLANGなどをみて標準的なlocaleに変化してくれれば良いのですが、リダイレクトの時にencodeが設定されないというのは、かなり悩ましい挙動です。

暗黙の挙動だから不定になるのは当然という意見もありそうですが、リダイレクトの時に挙動が切り替わるのは、やっぱり謎仕様です。rawデータとして何の処理もせずにbyte列を掃き出すなら、まだ分かるんですけどね。

ともかく無事に終わったので部屋の整理をして今日は休もうと思います。

0 件のコメント: