rayphe - 軽量Python web framework

  • 2010-2-10: v0.3.0リリースしました。またプロジェクト名を変更しています。

ちょっと前にRubyでSinatraが取り上げられて、結構注目されたように思います。ということはRailsだと大げさすぎるなあ、と思うような場合に対する需要というのはやっぱりそれなりにあるんですよね。

Pythonで軽量、というとweb.pyが一番有名ですよね。他にはJunoBottleなんかがあります。

このブログで使っているのはweb.pyです。結構昔から使っています。が、不満もおおくweb.pyを拡張するようなライブラリを作っていて、それがそこそこの量あったりします。

そこで、これくらい量があるなら自分でフレームワーク作っても大してかわんなくね?と思い始めました。あれ、そういえば俺、テンプレートエンジンもつくっちゃってるじゃん、簡易O/Rマッパも自分用につくってあるじゃん、と次々に気づき始め、それらをまとめて作っちゃいました。軽量フレームワーク。

rayphe

「rayphe」はPython用軽量ウェブフレームワークです。ルーティング、テンプレート、O/Rマッパなどが1ファイルにまとめられていて、依存するライブラリもありません。

また、共通の処理をまとめる「フィルタ」があったり、やアプリケーションの各フェーズをフックできたりと柔軟です。

raypheはGitHubにおいてあります。それなりにドキュメントも書いていて、テストもしてあります。詳しくは以下をどうぞ。

たとえば、サンプルアプリのコードの一部はこんな感じです。

python code
  1. with app.filter([context_setup_filter, {"except":["static_file"]}]):
  2.   @app.get("static/(unicode:.*)")
  3.   def static_file(c, path):
  4.     c.res.send_file(os.path.join(app.static_path, path.replace("..", "")))
  5.  
  6.   @app.get("")
  7.   def index(c):
  8.     c.res.redirect(app.url.show_pages())
  9.  
  10.   @app.get("page/(int:\d+)")
  11.   def show_page(c, page_id):
  12.     c.page = app.db.select_one_by_id(Page, page_id)
  13.     c.title += c.page.title
  14.     c.comments = app.db.select([Comment],
  15.       cond="page_id=? order by created_at asc",
  16.       values=[page_id])
  17.     c.comment = getattr(c, "comment", Comment(name="", body=""))
  18.     return app.renderer.show_page({"c":c})
  19.  

という感じでわかりやすくかけます。また、「単純化しすぎない」ということにも気を使っていたりします。selfと書くのが好きなPythonistaらしく、status 404とかじゃなくc.res.notfound()です。

というわけで

Pythonで小さなウェブアプリをつくるときは是非。

03.02.10/04am

Python版Yahooテキスト解析 APIライブラリをキーフレーズ抽出に対応させました

しました。

ダウンロード

yahooapi

使い方

python code
  1. import yahooapi.jlp
  2. client = yahooapi.jlp.KeyphraseServiceAPI("apikey")
  3. result_obj = client.extract(sentence=u"東京ミッドタウンから青山一丁目駅まで歩いて15分かかります")
  4. for result in result_obj.Result:
  5. print result.Keyphrase
  6.  
  7. # => 東京ミッドタウン
  8. # => 青山一丁目駅
  9. # => 15分
  10.  

うむ。

01.06.10/12am

埋め込み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

About

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

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

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

Pages