Google App Engineのテンプレートエンジン(Django 0.9.6)のfirstofタグが多バイト文字列に対応してない件
Google App Engineの個人的な印象
ということで,今のところ割と好きです.ただ,Google App Engineが現時点(2008年6月8日)ではまだPreview Releaseなので,今回,人柱の役目を果たそうと思います.
ちなみにこの問題のために1時間ぐらいはまりました.
djangoのテンプレートエンジンにおけるfirstof タグとは?
{% firstof var1 var2 var3 %}
こうするとvar1, var2, var3の順に変数を評価していって
- 最初に偽でないと評価された変数のみを(文字列化して)出力する
- 最後の変数まですべて偽だったら何も出力しない
というもの.
症状
firstofタグの引数に多バイト文字列な変数を与えると以下のようにUnicodeEncodeErrorがthrowされる
line 96, in render return self.template.render(context) File "/usr/local/google_appengine/google/appengine/ext/webapp/template.py", line 95, in _template_render_replacement return _original_template_render(self, context) File "/usr/local/google_appengine/lib/django/django/template/__init__.py", line 168, in render return self.nodelist.render(context) File "/usr/local/google_appengine/lib/django/django/template/__init__.py", line 705, in render bits.append(self.render_node(node, context)) File "/usr/local/google_appengine/lib/django/django/template/__init__.py", line 718, in render_node return(node.render(context)) File "/usr/local/google_appengine/lib/django/django/template/defaulttags.py", line 57, in render return str(value) UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
原因
以下に示す通り,
django 0.96のfirstofタグ
が多バイト文字列の環境に完全には対応していない.57行目にてsys.getdefaultencoding()の文字コードでデコードされると考えられるが,この値はPython2.5においてデフォルトのエンコーディングはasciiである.一方で,Google App Engineではsys.setdefaultencoding()にてデフォルトのエンコーディングを変更する方法も無いため,多バイト文字列が入力されるとUnicodeEncdeErrorがthrowされる.
46 class FirstOfNode(Node): 47 def __init__(self, vars): 48 self.vars = vars 49 50 def render(self, context): 51 for var in self.vars: 52 try: 53 value = resolve_variable(var, context) 54 except VariableDoesNotExist: 55 continue 56 if value: 57 return str(value) 58 return ''
対策
対策方法1
多バイト文字列を扱う場合は firstof タグを使わず通常の{% if var %}タグで処理する.ifタグだと文字列を与えなくてもdecodeしないため,UnicodeEncodeErrorはthrowされない.
対策方法2
Google App Engine付属のテンプレートエンジン(django 0.96相当)ではなく,後述のとおり本問題が解決されている最新開発版のdjangoを,自分のGoogle App Engineの領域にデプロイして,そちらのテンプレートエンジンを使う
補足
なお,2008年6月9日時点での最新開発版である
django trunk(revision 7557)のfirstofタグでは,このバグは修正されている模様である.83行目のsmart_unicodeという関数のネーミングに開発者の苦労が忍ばれる.
72 class FirstOfNode(Node): 73 def __init__(self, vars): 74 self.vars = map(Variable, vars) 75 76 def render(self, context): 77 for var in self.vars: 78 try: 79 value = var.resolve(context) 80 except VariableDoesNotExist: 81 continue 82 if value: 83 return smart_unicode(value) 84 return u''