埋め込み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 %>でフィルターをオフにできます。

      python code
      1. Embpy("<%= b %>", filter=cgi.escape).render({"v":"<b>"})
      2. # => "&lt;b&gt;"
      3. Embpy("<%=r b %>", filter=cgi.escape).render({"v":"<b>"})
      4. # => "<b>"
      5.  
      6. result = Embpy("<%=r b %>", filter=cgi.escape).render({"v":"<b>"})
      7. # result.__class__ => EmbpyString
      8. Embpy("<%= b %>", filter=cgi.escape).render({"v":result})
      9. # => "<b>"
      10.  
    • 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の代わりに<% %>も使えるようにしました。

       code
      1. <%- if True: -%>
      2.    ok
      3.    <% %>
      4.  

    と書けるようになりました。

  • 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重でフィルタが適応されない。
  • もちろんマルチバイトでも大丈夫。

といったところが特徴ですかね。

テンプレートはこんな感じにかけます。

 code
  1. <%-
  2. class Hoge(object):
  3.   def __init__(self):
  4.     pass
  5.   end
  6. end
  7. hoge = Hoge()
  8. a = "< title >"
  9. -%>
  10. <%=r a %>
  11. <%- def format(v) {: return "%4d"%v; :} -%>
  12. <%- def format2(v) {: return "%2d"%v; :} -%>
  13.   <% for y in xrange(1,xx):%><%= format(y) %><% end %>
  14. <%- for x in xrange(1,xx): -%>
  15. <%= format2(x) -%>
  16.   <%- for y in xrange(1,xx): -%>
  17. <%= format(x*y) -%>
  18.   <%- end %>
  19. <%- end -%>
  20.  

んでこんな感じに使います。第1引数にはファイルではなく文字列も渡せます。

python code
  1. e = embpy.Embpy(codecs.open("path_to_template", encoding="utf8"),
  2.           cache_path = "path_to_cache_file",
  3.           template_globals = {}, filter=cgi.escape)
  4. print e.render({"xx": "10"})
  5.  

出力はこう。

 code
  1.     1 2 3 4 5 6 7 8 9
  2. 1 1 2 3 4 5 6 7 8 9
  3. 2 2 4 6 8 10 12 14 16 18
  4. 3 3 6 9 12 15 18 21 24 27
  5. 4 4 8 12 16 20 24 28 32 36
  6. 5 5 10 15 20 25 30 35 40 45
  7. 6 6 12 18 24 30 36 42 48 54
  8. 7 7 14 21 28 35 42 49 56 63
  9. 8 8 16 24 32 40 48 56 64 72
  10. 9 9 18 27 36 45 54 63 72 81
  11.  

ダウンロード

embpy

コード

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

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

01.06.10/12am

XML2JSON+javascriptテンプレートエンジンでRSS表示

drk7.jpさんでXML2JSON serviceという面白いサービスが開始されました。

ということで早速ハックしてみました。

前のエントリーで書いたJavascriptテンプレートと組み合わせてめちゃくちゃ簡単にRSSを表示できます。

サンプルはこちら

使い方ですが、多分ソースをみてもらったほうが速いです。

//サンプルHTMLから抜粋

javascript code
  1. Event.observe(window, "load", function(){
  2.         new XML2JSON("<a href="http://feeds.feedburner.com/Inforno">http://feeds.feedburner.com/Inforno</a>", {container: "test", template : {element:"template"}});
  3. });
  4.  

初期化コードはコレだけです。 containerでinnerHTMLに結果を流し込みたいElementを指定します。 templateはLyase.View.renderの引数と同じです。 例ではtextareaエレメントのid:"tempalte"のinnerHTMLをテンプレートとして扱います。

html code
  1. //テンプレート
  2.   <textarea id="template" style="display:none;">
  3.  
  4. <h3>< %= context.channel.title %></h3>
  5.  
  6. <ul class="items">
  7.       < % context.channel.item.each(function(item,index){%>
  8.  
  9.     <li><a href="<%= item.link%>" target="_blank">< %= item.title%></a></li>
  10.         < % if(index > 5){throw $break;}%>
  11.       < %})%>
  12.    </ul>
  13.   </textarea>
  14.  

こんな感じでテンプレートがかけるので、デザインの変更が大変楽になります。 またJSONデータ取得時のonloadを好きなメソッドに置き換えることもできるのでLyase.Viewを使わなくてもXML2JSONライブラリだけで おいしくいただけます。すきに改造してやってください、たいしたものじゃないですし(笑

07.27.08/12am

埋め込みjavascriptを実装してみました。

naoyaさんのJemplateの記事に触発されて埋め込みjavascriptを実装してみました。

同様の実装としてAjax Pagesがあるのですが、よりシンプルに実装してみました。コードにして50行。

サンプルファイルダウンロード

これは今作っているフレームワークの一部です。シンプルですがわりと使えるかと。 実際の動作はサンプルファイルを見ていただくとして

lyase_view.jsの説明どおり

javascript code
  1. /*
  2. * using innerHTML
  3. * -------------
  4. * //in HTML
  5. * <textarea id="template" style="display:none">
  6. * The value of x is:< %= context.x%>
  7. * </textarea>
  8. * //code
  9. * document.write(Lyase.View.render({element:"template"}, {x : 10}));
  10. *
  11. * using a template file
  12. * -------------
  13. * //in /template.jhtml
  14. * The value of x is:< %= context.x%>
  15. * //code
  16. * document.write(Lyase.View.render({file:"/template.jhtml"}, {x : 10}));
  17. *
  18. * Of course, you can embed more complex codes.
  19. * //in HTML, with prototype.js
  20. * <textarea id="template" style="display:none">
  21. * < % context.list.each(function(pair){%>
  22. * The value of < %= pair.name %> is: < %= pair.value%>
  23. * < % }) %>
  24. * </textarea>
  25. * //code
  26. * document.write(Lyase.View.render({element:"template"},
  27. * {list :[{name : "x", value : 10}, {name : "y", value : 20}]}));
  28. */
  29.  

こんな感じでjavascriptのコードが読み込めます。

また

html code
  1. <h1>< %= context.title %></h1>
  2.  この文章はLyase.Viewを使用して< %= context.fileName %>の内容を表示しています。
  3.  
  4.  
  5. 以下のようにクロージャも問題なく使用できます。
  6.  
  7. < % var c = "," %>
  8. < % context.list.each(function(num){ %>
  9.   < %= num+c %>
  10.  
  11. < % })%>
  12.  
  13. < %= render({file :"./sample2.jhtml"}, context)%> <!--ココに注目--!>
  14.  

このようにテンプレート内で別のテンプレートを呼び出すことも可能です。

テンプレートには文字列、textareaなどのinnerHTML、テンプレートファイルが使用できます。 prototype.jsのAjax.Updaterあたりをハックすれば結構使えるものになるんじゃないかなあ、と思ってみたり。

・2006年2月16日追記 id:brazilさんもjavascriptのテンプレートについて書かれてますね。 実は僕もHTML生成関数は作成しています。DOM版とHTMLタグ生成版で。 今回のライブラリのようなアプローチとどっちがいいか、となるともう少し検討が必要かな・・・

07.27.08/12am

About

Author:yuin(http://inforno.net/)

文学部文化学科卒という生粋の文系趣味プログラマ。

主にRuby、Javascript、PHP、JAVA,Python,C,Scala,Schemeなどを使っています。今はPythonな感じかもしれない。今後作曲活動なども復活するかもしれない。

Pages