埋め込みPythonを実装してみました

更新履歴

  • 2009/02/20 version 1.0.0

    • RendererHelper を追加。詳しくはソースファイルヘッダ部分のドキュメントを参照してください。
  • 2009/02/17 version 0.5.0

    • <%= %> で自動的にフィルタを適応できるようになりました。また、 render メソッドが unicode オブジェクトではなく unicode のサブクラス EmbpyString オブジェクトを返すようになりました。filterは EmbpyString オブジェクトをスルーします。これにより2重でfilterが適応されることがなくなります。<%=r %>でフィルターをオフにできます。
Embpy("<%= b %>", filter=cgi.escape).render({"v":"<b>"})
# => "&lt;b&gt;"
Embpy("<%=r b %>", filter=cgi.escape).render({"v":"<b>"})
# => "<b>"

result = Embpy("<%=r b %>", filter=cgi.escape).render({"v":"<b>"})
# result.__class__ => EmbpyString
Embpy("<%= b %>", filter=cgi.escape).render({"v":result})
# => "<b>"
  • re.Scannerscan メソッドがスレッドセーフだったのでインスタンスをモジュールグローバルにしました。また、 re.Scanner インスタンスの初期化をLazyにしました。このことにより re.Scanner インスタンスの生成数が減り、さらにキャッシュのみの利用時にはインスタンスを生成しないのでパフォーマンスが向上しました。
  • 2009/02/16 version 0.4.0

    • パフォーマンス改善
  • 2009/02/15 version 0.3.0

    • epy の中の人から「end.. orz」という反応をいただいたので、endの代わりに <% %> も使えるようにしました。
<%- 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):
    pass
  end
end
hoge = 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"})

ダウンロード

embpy

コード

先読みはいらないので、 re.Scanner で一発。

あと "(((?<=\\)")|[^"])\*((?<!\\)")" という正規表現は自分的には常套句。”で囲まれていて\“は”自身を表す、というよくある文字列の仕様に使える正規表現です。

comments powered by Disqus