Doge log

Abby CTO 雑賀 力王のオフィシャルサイトです

MakoのTagを追加する

MakoのTagの追加方法を誰も書いていないようなので適当に書いてみる。

from mako.parsetree import Tag
from mako.template import Template
from mako.codegen import _GenerateRenderMethod

class TestTag(Tag):
    __keyword__ = 'test'

    def __init__(self, keyword, attributes, **kwargs):
        super(TestTag, self).__init__(keyword, attributes, (), (), (), **kwargs)

    def accept_visitor(self, visitor):
        def traverse(node):
            for n in node.get_children():
                n.accept_visitor(visitor)
        method = getattr(visitor, "visit" + self.__class__.__name__, traverse)
        if isinstance(visitor, _GenerateRenderMethod):
            self.content = "replace text"
            visitor.visitText(self)
        else:
            method(self)

str_tmpl = """
test sample
<%test>
Hello
</%test>
"""

tmpl = Template(str_tmpl)
print tmpl.render()

レンダリング結果はこうなる

test sample
replace text

本当は子ノードのcontentを置き換えるとかでいいんだけど直書きもできるよってサンプル。
とりあえずMakoの場合はレンダリングまでの仕組みが重要。

  1. Lexerを使い、正規表現ベースでパース
  2. nodeのtree生成
  3. nodetreeに対してvisitorパターンでなめる
  4. 各nodeでpythonコードの断片を出力
  5. pythonコードをpythonモジュール化
  6. モジュールの実行でレンダリング

Makoが高速な理由はjspと同じ方法をとっているから。
つまりテンプレートをpythonコードに置き換え、モジュールとして実行している。
(パースが入る初回はちょっと遅い)
なので注意する点としてはvisitorで書き出すのはテキストではなくpythonコード断片であるという点。

でも実際のところ、カスタムTagの追加はMakoのドキュメントに出てるわけでもないので、内部を知らないと書けないんだけどねー。
MakoはカスタムTagなんて要らないくらいキョーレツーな仕掛けがあるので書いてないだけ。
ちなみにみんな大好きjinja2は拡張方法がドキュメント化されてますよ。