jinja2の簡単な原理(exec関数の使われる実例)
python製のテンプレート・エンジンjinja2がどんな処理しているのか勉強してみました。メタクラス,ジェネレーターなどpythonらしい書き方をいくつかおぼえることができました。あと、exec関数の使い方。これは、pythonらしいのか不明ですが、jinja2で具体的な使い方を学ぶことができます。これから、jinja2の処理の流れを紹介しますが、それはある意味exec関数の使い方の例の紹介になっていると思います。
jinja2はその他の処理系と同様にソースコード(テンプレート)をLexerでトークン化し、それをParserでAstNodeに変換します。それをさらにCodeGeneratorがpython codeに変換します。これをexec code in namespaceで実行してから、code中に定義されているrootという関数をnamespaceから取り出し実行することによってrenderingします。python codeを生成するところまで書くのは大変ですし、ある意味決まり切った処理ですが、python codeを与えてから、レンダリングするまでは簡単ですので、ここに簡略化したコードを書いておきます。まず、テストコードはこんな感じになります。
#!/usr/bin/env python2.6 import unittest from test import test_support import MY_jinja2 class TemplateTests(unittest.TestCase): def test_template_from_code(self): code = """\ from MY_jinja2.runtime import to_string def root(dic): l_seq = dic['seq'] for l_item in l_seq: yield to_string(l_item) """ tmpl = MY_jinja2.Template.from_code(code) self.assertEqual("0123456789", tmpl.render(seq = range(10))) if __name__ == '__main__': test_support.run_unittest(TemplateTests)
ここからがソースコード本体になります。
- MY_jinja2/__init__.py
from MY_jinja2.environment import *
- MY_jinja2/environment.py
class Template(object): @classmethod def from_code(cls, code): tpl = object.__new__(cls) namespace = {} exec code in namespace tpl.render_func = namespace['root'] return tpl def render(self, *args, **kwargs): dic = dict(*args, **kwargs) return ''.join(self.render_func(dic))
- MY_jinja2/runtime.py
to_string = unicode