kamoというテンプレートエンジンを作ろうとしてみました
kamoというテンプレートエンジンを作ってみました
kamo というテンプレートエンジンを作ってみました。まだ開発は途中なのですが、だいたい1日位でそれなりに動くテンプレートエンジンが作れるものなんだな〜と思ったりしました。文法的にはおおよそmako に似せていてmakoのsubsetのような感じです。
名前のkamoもアナグラムなのでてきとうに付けた名前です。
発端というか動機はあまり無く、なんとなく素朴にテンプレートエンジンが作りたくなり、1日でどれ位作れるのかちょっと気になったので、作ったという流れです。
以下のようなテンプレートが動きます。
<%doc> cal.kamo </%doc> <% import calendar from datetime import date month = ["","睦月","如月","弥生","卯月","皐月","水無月","文月","葉月","長月","神無月","霜月","師走"] def paren(i): return "({})".format(i) %> # ${today.year}年 ======================================== %for i in range(1, 13): ${i}月${month[i]|paren} ---------------------------------------- %for d in range(1, calendar.monthrange(today.year, i)[1]): %if date(today.year, i, d) <= today: - ${d} ☓ %else: - ${d} %endif %endfor %endfor
機能な話
機能は以下の様な最小限のもの。
- makoのようにテンプレート中にpython codeを直接書ける機能がついています。
- 条件分岐(if),ループ(for)の構文があります
- コメントがあります
- フィルター("${xxx|foo}"的なもの)があります
- 実行する度にテンプレートを走査して実行せず、初回呼び出し時にpython code(関数)を生成してそれを利用します。
直接python codeを書ける部分は他のテンプレートエンジンとは異なるかもしれないです(これにより変数参照の管理をしないとダメになった)。 あと、最近のテンプレートエンジンと同じように、実行毎にテンプレートを走査して描画せずに、出力されたpython codeを呼び出す感じで実行されます。
使い方はだいたい他のテンプレートエンジンと同じ感じです。
from kamo import TemplateManager from datetime import date tm = TemplateManager(directories=["."]) tm.lookup("cal.kamo").redner(today=date.today())
以下の様な実行結果になります。
# 2015年 ======================================== 1月(睦月) ---------------------------------------- - 1 ☓ - 2 ☓ # skip ... - 29 ☓ - 30 ☓ # skip 12月(師走) ---------------------------------------- - 1 - 2 - 3 # skip ... - 28 - 29 - 30
内部的な話
ちなみに先ほどのテンプレートは内部的には以下のようなコードに変換されます。これはloggerのdebugオプションを付けると見ることができます。
def render(io, **c): write = io.write ######################################## # cal.kamo ######################################## import calendar from datetime import date month = ["","睦月","如月","弥生","卯月","皐月","水無月","文月","葉月","長月","神無月","霜月","師走"] def paren(i): return "({})".format(i) write('# ') write(str(c['today'].year)) write('年\n========================================\n') for i in range(1, 13): write(str(i)) write('月') write(str(paren(month[i]))) write('\n----------------------------------------\n') for d in range(1, calendar.monthrange(c['today'].year, i)[1]): if (date(c['today'].year, i, d) <= c['today']): write('- ') write(str(d)) write(' ☓\n') else: write('- ') write(str(d)) write('\n')
わりと式のパースがだるくて、結局pythonのastモジュールの力を借りました。あと、python codeの生成は以前作っていたprestringを利用したら結構手軽に出来ました。