更新履歴
2009/02/20 version 1.0.0
-
RendererとHelperを追加。詳しくはソースファイルヘッダ部分のドキュメントを参照してください。
-
2009/02/17 version 0.5.0
<%= %>で自動的にフィルタを適応できるようになりました。また、renderメソッドがunicodeオブジェクトではなくunicodeのサブクラスEmbpyStringオブジェクトを返すようになりました。filterはEmbpyStringオブジェクトをスルーします。これにより2重でfilterが適応されることがなくなります。<%=r %>でフィルターをオフにできます。python codeEmbpy("<%= b %>", filter=cgi.escape).render({"v":"<b>"})# => "<b>"Embpy("<%=r b %>", filter=cgi.escape).render({"v":"<b>"})# => "<b>"result = Embpy("<%=r b %>", filter=cgi.escape).render({"v":"<b>"})# result.__class__ => EmbpyStringEmbpy("<%= b %>", filter=cgi.escape).render({"v":result})# => "<b>"
re.Scannerのscanメソッドがスレッドセーフだったのでインスタンスをモジュールグローバルにしました。また、re.Scannerインスタンスの初期化をLazyにしました。このことによりre.Scannerインスタンスの生成数が減り、さらにキャッシュのみの利用時にはインスタンスを生成しないのでパフォーマンスが向上しました。
2009/02/16 version 0.4.0
- パフォーマンス改善
2009/02/15 version 0.3.0
epyの中の人から「end.. orz」という反応をいただいたので、endの代わりに
<% %>も使えるようにしました。code<%- if True: -%>ok<% %>
と書けるようになりました。
2009/02/14 version 0.2.0
- "{" と "}"が辞書リテラルとかぶっていたので、"{:"と":}"に変更しました。
-
変換後コードでなく、コンパイル済みcodeオブジェクトをキャッシュするようにしました。
はじめに
このブログはweb.pyで作られており、テンプレートエンジンもweb.py標準のものを使っています。でもこのweb.pyのテンプレートエンジン、罠が多い。なので他のテンプレートエンジンに置き換えようかなあ、とか思ってました。
んで個人的にはわざわざテンプレート用に文法覚えるのはめんどいので、埋め込み形式でコードが短くて軽そうなのはないかと探したところ、epyがヒット。
ただ、この実装%>が文字列の中にあると動かなかったり(a= "hoge%>"みたいな)、コードの短さゆえに割り切っている部分が多いので同じくらい短いコードでもうちょっと高機能版を実装してみました。以前紹介したre.Scannerを活用すれば、見やすいコードで短く実装できました。
- キャッシュ
-
インラインでPythonを書くことも出来る:
def format(v) {: return "%4d"%v; :}みたいに。 - eRubyのtrim modeの"<%-"と"-%>" : これがあると無いではテンプレートの見易さが段違い。
- 自動的にフィルタを適応。しかも2重でフィルタが適応されない。
- もちろんマルチバイトでも大丈夫。
といったところが特徴ですかね。
例
テンプレートはこんな感じにかけます。
<%-class Hoge(object):def __init__(self):passendendhoge = Hoge()a = "< title >"-%><%=r a %><%- def format(v) {: return "%4d"%v; :} -%><%- def format2(v) {: return "%2d"%v; :} -%><% for y in xrange(1,xx):%><%= format(y) %><% end %><%- for x in xrange(1,xx): -%><%= format2(x) -%><%- for y in xrange(1,xx): -%><%= format(x*y) -%><%- end %><%- end -%>
んでこんな感じに使います。第1引数にはファイルではなく文字列も渡せます。
e = embpy.Embpy(codecs.open("path_to_template", encoding="utf8"),cache_path = "path_to_cache_file",template_globals = {}, filter=cgi.escape)print e.render({"xx": "10"})
出力はこう。
1 2 3 4 5 6 7 8 91 1 2 3 4 5 6 7 8 92 2 4 6 8 10 12 14 16 183 3 6 9 12 15 18 21 24 274 4 8 12 16 20 24 28 32 365 5 10 15 20 25 30 35 40 456 6 12 18 24 30 36 42 48 547 7 14 21 28 35 42 49 56 638 8 16 24 32 40 48 56 64 729 9 18 27 36 45 54 63 72 81
ダウンロード
コード
先読みはいらないので、re.Scannerで一発。
あと"(((?<=\\)")|[^"])\*((?<!\\)")"という正規表現は自分的には常套句。"で囲まれていて\"は"自身を表す、というよくある文字列の仕様に使える正規表現です。