SlideShare une entreprise Scribd logo
1  sur  47
Télécharger pour lire hors ligne
Pylons ユーザのための Pyramid
移行ガイド
Author:        Nozomu Kaneko
               Django & Pyramid Con JP 2012
Publication:
               (in conjunction with Pycon JP 2012)
Date:          2012-09-16




                                                     #1
お前誰よ


•   金子望 (Twitter: @knzm2011)
•   勤務先: トライアックス株式会社
•   Pylons を使い始めて5年ぐらい
    •   Python 歴もだいたい同じくらい
    •   入社後に Python を使い始めて、気がついたら CMS フレームワークを作ってた
•   翻訳とか
    •   Python ドキュメント日本語翻訳プロジェクト
    •   PEP 333: Python Web Server Gateway Interface v1.0
    •   Pylons 0.9.7 ドキュメント
    •   Pylons プロジェクトドキュメント / Pyramid ドキュメント
•   Pylons Project JP の中の人
•   実は Pyramid はあまり使ってない (これから...)                          #2
アジェンダ
•   第1部 Pyramid の概要
    •   Pyramid FAQ
    •   Pylons に何が起こったか
    •   Pylons 1.x の何が悪かったのか
    •   Pyramid の特徴
•   第2部 移植の方針
    •   Pylons 1.0 プロジェクトを Pyramid に移植すべき3つの理由
    •   Pylons 1.0 プロジェクトを Pyramid に移植すべきでない理由
    •   移植方法
    •   移植における注意点
    •   移植デモ
•   第3部 Pylons と Pyramid の比較

                                                 #3
第1部 Pyramid の概要
Pyramid FAQ
•   Pyramid と Pylons の関係は?
    •   repoze.bfg と Pylons 1.0 という 2 つのウェブフレームワークが合流して
        Pyramid ができた
        •   コードベースは repoze.bfg
        •   Pyramid を開発しているプロジェクトの名前は Pylons プロジェクト
    •   ウェブフレームワークとしての Pylons もまだ現役


•   repoze.bfg とは何ですか?
    •   repoze = Zope 由来のコンポーネントを WSGI アプリケーションで利用
        できるようにしたコンポーネント集
    •   repoze.bfg = repoze のコンポーネントを再構成したフレームワーク



                                                          #5
Pyramid FAQ
•   Python 3 で動きますか?
    •   Pyramid 1.3 から Python 3.2 以上で動きます (thanks to @aodag)


•   Pylons 1.0 プロジェクトを Pyramid に移植すべきですか?
    •   後で詳しく説明します




                                                               #6
Pylons に何が起こったか
•   Pylons 1.0 までは順調に開発が進んだ
    •   特に、周辺ライブラリも含めた Pylons スタックは WSGI ベースの
        フレームワークとして代表的な存在に
•   拡張性を取り入れるために改良しようとして、問題があることが分かった
    •   Ben Bangert による blog 記事 (2010年11月): “Why Extending Through
        Subclassing (a framework’s classes) is a Bad Idea”
•   Pylons 2 の開発を進めるうちに repoze.bfg と似てきたことで、コードベース
    と開発コミュニティをマージする方向へとシフト
•   2011年1月31日 Pyramid 1.0 リリース




                                                                 #7
Pylons 1.x の何が悪かったのか
•   サブクラス化によるフレームワークの拡張
    •   フレームワークを改良したくても、すべての主要なメソッドは事実上の API
        として凍結されていた
        •   Pylons では、フレームワークの提供するベースクラスをサブクラス化
            することでプロジェクトを作成 (BaseController, BaseWSGIApp)
        •   ユーザはカスタマイズが必要なメソッドを自由にオーバーライドする
    •   深い継承によるパフォーマンスの問題
    •   フレームワーク内の親クラスに依存するため単体テストしづらい
    •   多重継承による奇妙な衝突が発生
•   StackedObjectProxy の使用
    •   スレッドローカルかつアプリケーション固有のグローバル変数
    •   時に混乱の原因となる


                                                           #8
Pyramid の特徴
•    Pylons と repoze.bfg それぞれに由来する豊富な機能 (※)
     •   ルーティング: URLディスパッチ or トラバーサル
     •   データベースエンジン: SQLAlchemy or ZODB
     •   テンプレートエンジン: Mako or Chameleon
     •   scaffold
     •   インタラクティブデバッガー
     •   セキュリティ (Authentication / Authorization)
•    これまでのフレームワーク開発で得られた教訓
     •   徹底したテストコード
     •   徹底したドキュメンテーション (API と実装の分離)
     •   サブクラス化に頼らない拡張方法
     •   グローバル変数の排除

    ※ フレームワークが乱立することを防ぐため、 Pyramid ではフレームワーク内で
                                                   #9
    ある程度の機能の重複があることは想定内とされている
第2部 移植の方針
Pylons 1.0 プロジェクトを Pyramid に移植すべき
3つの理由
•   Pylons 1.0.x は「レガシー」扱いで今後はメンテナンスのみ
    •   今後 Pylons に大きな機能追加はない
•   Pyramid の方が (色々な意味で) 強力
    •   特に拡張性が非常に高いのでメタフレームワークとして魅力的
•   Pyramid for Pylons Users が公開された (2012-06-12)
    •   翻訳済み: http://docs.pylonsproject.jp
        /projects/pyramid_cookbook-ja/en/latest/pylons/index.html




                                                                    #11
Pylons 1.0 プロジェクトを Pyramid に移植すべきでない
理由
•   Pyramid 自体が開発途上
    •   例) 1.3.2 で Mako テンプレートの継承ができなくなる致命的なバグがあり、
        修正版 (1.3.3) がリリースされるまで 3 ヶ月近くまともに使えなかった
    •   全体的に Pylons ユーザ向けの機能はまだ弱い
•   情報が少ない
    •   Pylons (特に大規模プロジェクト) からの移行に関してはあまり情報がない
    •   日本では Pyramid ユーザ自体が少ない (~30人ぐらい?)
    •   Pylons Project JP http://www.pylonsproject.jp/ にぜひ参加を (宣伝)




                                                                     #12
移植する? しない?
•   注: 個人の感想です
    •   Pyramid と Pylons は内部がかなり違うので、移植はそれなりに覚悟が必要
    •   まずは新規のプロジェクトで Pyramid を試してみる
        •   うまくいったら小規模なプロジェクトに適用
    •   既存のプロジェクトを移植する場合は、今のうちから計画的に準備しておくといいかも
        •   テストを書く
        •   コントローラを軽くする
        •   コントローラ以外の場所ではグローバル変数 pylons.request を使わない
        •   Python 2.6 or 2.7 に環境をアップデートする
        •   etc.




                                                   #13
移植の戦略 (1) ゼロから Pyramid で書き直す
•   あまり変更せずに再利用可能
    •   モデル, テンプレート, 静的ファイル


•   変更が必要
    •   コントローラ, ルーティング, グローバル変数




                                  #14
移植の戦略 (1) ゼロから Pyramid で書き直す
BeforeRender イベントを使ってレンダラーグローバル変数を追加する例:

from pyramid.events import subscriber
from pyramid.events import BeforeRender
from pyramid.threadlocal import get_current_request

from mypylonsproject.lib import helpers

@subscriber(BeforeRender)
def add_renderer_globals(event):
   event["h"] = helpers
   request = event.get("request")
   if request is None:
       request = get_current_request()
   event["c"] = request.tmpl_context

                                                      #15
移植の戦略 (2) 共存させつつ徐々に移植する
•   一度に 1 つずつ URL を移植する
    •   移植された URL -> Pyramid
    •   移植されていない URL -> Pylons
•   選択肢
    •   A) mod_rewrite を使用する
        •   Pyramid と Pylons の両方のアプリケーションが別プロセスで実行される
    •   B) INI ファイルの中で paste.cascade を設定する
        •   最初に片方のアプリケーションを実行してみて、 “Not Found” が
            返る場合にはもう片方のアプリケーションを試す
        •   Pylons が静的 ファイルを返すのと同じ方法
    •   C) Pyramid のビューで Pylons アプリケーションをラップする



                                                    #16
移植の戦略 (2) 共存させつつ徐々に移植する
NotFound ビューを使用する例:

from mypylonsproject import thepylonsapp

class LegacyView(object):
   def __init__(self, app):
       self.app = app
   def __call__(self, request):
       return request.get_response(self.app)

if __name__ == '__main__':
  legacy_view = LegacyView(thepylonsapp)
  config = Configurator()
  config.add_notfound_view(legacy_view)
  # ... rest of config ...

                                               #17
移植における注意点
•   複数のアプリケーションを同時に実行する際に調整が必要な箇所
    •   データベース接続数、セッションの共有、ファイルのロックなど
•   Pyramid アプリケーションを Python 2 と 3 のどちらで書くかは重要
    •   Python 3
        •   Pyramid は Python 3 対応済み
        •   Pyramid が必要とするライブラリもほとんどは Python 3 対応している
        •   一部のライブラリは Python 3 対応していない
            •   PIL, Paste, WebHelpers, FormEncode, Pylons
    •   Python 2
        •   バージョンは 2.6 以上必須
            •   Pyramid は Python 2.6 以上で動作
            •   多くのライブラリで Python 2.5
                以下は徐々にサポートが打ち切られている
                                                             #18
•   レガシー Pylons アプリケーションで使用しているライブラリのバージョンが 古いと
    Pyramid との共存が難しい




                                             #19
実際にやってみた
•   Pylons の QuickWiki チュートリアルを Pyramid に移植
    •   「ゼロから Pyramid で書き直す」パターン
•   https://bitbucket.org/knzm/quickwiki-pyramid/wiki/PatchList




                                                                  #20
第3部 Pylons と Pyramid の比較
Pylons 流のアプリケーション開発が、 Pyramid に移行することでどう変わるか
paster コマンド -> p* コマンド
Pylons             Pyramid             備考
paster create      pcreate             オプション -t => -s
paster serve       pserve               
paster shell       pshell              初期設定される変数が違う
paster setup-app   initialize_App_db   App はアプリケーション名




                                                   #22
scaffold
•   新しくプロジェクトを開始する場合
    •   Pylons: paster create -t <テンプレート名>
    •   Pyramid: pcreate -s <scaffold 名>


•   標準 scaffold
    •   alchemy: URL ディスパッチ、 SQLAlchemy
    •   starter: URL ディスパッチ、データベースなし
    •   zodb: トラバーサル、 ZODB




                                             #23
ディレクトリレイアウト
alchemy scaffold でプロジェクトを作成した場合

PyramidApp/
├── development.ini, production.ini
├── setup.py, setup.cfg
├── pyramidapp/
│   ├── __init__.py, models.py, views.py, tests.py
│   ├── templates/
│   ├── static/
│   └── scripts/
└── PyramidApp.egg-info/

•   変更点
    •   controllers, config, lib がなくなった
    •   views が増えた
    •   public が static に名前が変わった
                                                     #24
    •   アプリケーションの設定が __init__.py に集約された
main 関数
•   アプリケーションを返すトップレベルの関数
    •   Pylons: pylonsapp/config/middleware.py の make_app 関数
    •   Pyramid: pyramidapp/__init__.py の main 関数
•   Pyramid の main 関数は Pylons の middleware.py, environment.py,
    routing.py の内容を含む
•   Pyramid では Configurator を使って Pylons よりも簡潔に設定が行える
•   WSGI ミドルウェアはありません




                                                                 #25
main 関数
Pyramid の main 関数の例:

from pyramid.config import Configurator

def main(global_config, **settings):
   """ This function returns a Pyramid WSGI application.
   """
   config = Configurator(settings=settings)
   config.add_static_view('static', 'static', cache_max_age=3600)
   config.add_route('home', '/')
   config.scan()
   return config.make_wsgi_app()




                                                                    #26
ルーティングとビュー
•   Pylons の場合
    •   ルーティングの登録は map.connect() で行う
        •   map.connect('/article/{id}', controller='article', action='show')
    •   controllers ディレクトリの同名のコントローラが呼ばれる
    •   コントローラは BaseController を継承したクラス
•   Pyramid の場合
    •   ルーティングの登録とビューの登録が分かれている
        •   ルーティングの登録
            •   config.add_route('article_page', 'article/{id}')
        •   ビューの登録
            •   config.add_view() または @view_config デコレータ
    •   ルーティングは必ず名前を持つ
    •   ビューは任意の callable オブジェクトを指定できる
        •   関数、クラス、 __call__ メソッドを 実装したインスタンス                            #27
view_config のパラメータ
•   述語引数
    •   route_name
    •   context
    •   request_method
    •   request_param
    •   match_param
    •   custom_predicates
•   非述語引数
    •   renderer
    •   permission
    •   http_cache



                            #28
view_config のパラメータ
•   述語引数
    •   route_name - ルート名
    •   context - 例外ビューなどで使用
    •   request_method - GET とか POST とか
    •   request_param - key=val
    •   match_param - ルーティングの変数部分 (/{title} とか)
    •   custom_predicates - 任意の関数
•   非述語引数
    •   renderer
    •   permission
    •   http_cache



                                                  #29
リソース
•   「トラバース」というルーティング方式を使う場合に重要な概念
•   URL ディスパッチ (Pylons と同様のルーティング方式) を使う場合は、
    あまり詳しく知る必要はない
    •   重要なのは root リソースのみ
•   使い方
    •   ビジネスロジックの定義
    •   セキュリティ




                                               #30
リソースの使用例
class Resource(object):
   __acl__ = [
       (Allow, Everyone, 'view'),
       (Allow, 'group:editors', 'edit'),
       ]

   def __init__(self, request):
       self.request = request

config = Configurator(settings=settings, root_factory=Resource)

個別のルーティングで使うリソースを指定する場合

config.add_route('abc', '/abc', factory=Resource)


                                                                  #31
特殊グローバル変数
pylons.request
•   ビュー関数の中では request 引数
•   クラスベースのビューメソッドの中では self.request
•   テンプレートの中では、 request あるいは req
•   それ以外の場所では pyramid.threadlocal.get_current_request()
    を呼び出して request を取得可能
    (pshell やユニットテストなどで request オブジェクトが渡ってこない場合)


pylons.response
•   Pyramid にはグローバルなレスポンスオブジェクトはない




                                                          #32
特殊グローバル変数
pylons.tmpl_context, pylons.c
•   テンプレートに変数を渡したい場合、ビューから dict を返すとレンダラー経由で
    テンプレートに渡る
•   ※過去に request.tmpl_context が導入されたが、後に廃止された




                                                #33
特殊グローバル変数
pylons.app_globals
•   最も近い等価物は request.registry か request.registry.settings
•   レジストリはアプリケーションに関する設定を集約するために Pyramid
    内部で使用されるオブジェクト
•   request.registry.settings は通常アプリケーションの設定が格納される




                                                            #34
特殊グローバル変数
pylons.url (h.url_for)
•   request が URL 生成のためのメソッドを持っている
    •   request.route_url()
    •   request.static_url()
    •   request.resource_url()




                                     #35
特殊グローバル変数
pylons.session
•   request.session
•   pyramid_beaker 拡張を有効にするか、 Configurator に session_factory
    を渡す
from pyramid.session import UnencryptedCookieSessionFactoryConfig
session_factory = UnencryptedCookieSessionFactoryConfig('secret')
config = Configurator(session_factory=session_factory)


pylons.cache
•   Pyramid はキャッシュ機能を内蔵していない
•   pyramid_beaker 拡張を使用する


                                                               #36
HTTP エラーとリダイレクト
Pylons の場合 (コントローラの中で):

abort(404)   # Not Found
abort(500)   # Internal server error
redirect(url("section1"))   # リダイレクト

Pyramid の場合 (ビューの中で):

raise exc.HTTPNotFound()            # Not Found
return exc.HTTPNotFound()           # 戻り値として返すこともできる
raise exc.HTTPInernalServerError()  # Internal server error
raise exc.HTTPFound(request.route_url("section1"))   # リダイレクト

HTTNotFound と HTTPForbidden は Pyramid 内部でも発生する




                                                                #37
例外ビュー
特定の例外が起きたときに呼び出されるビューのこと。

class ValidationFailure(Exception):
   def __init__(self, msg):
       self.msg = msg

@view_config(route_name='home')
def home(request):
   raise ValidationFailure('some error')

@view_config(route_name='home', context=ValidationFailure)
def failed_validation(exc, request):
   return Response('Failed validation: %s' % exc.msg)



                                                             #38
静的ファイル
•   Pyramid で静的ファイルを返す標準の方法

    config.add_static_view('static', 'static', cache_max_age=3600)

    •   静的 URLにプレフィックス “/static” が付く
    •   トップレベルのファイル URL を返せない
•   どうするか
    •   favicon.ico

        <link rel="shortcut icon"
        href="${request.static_url('pyramidapp:static/favicon.ico')}" />

    •   robots.txt

        Alias /robots.txt /var/www/static/norobots.txt


                                                                      #39
静的ファイル
•   高度な使い方
    •   複数のパスを設定する
        •   config.add_static_view(name='images', path='static/images')
        •   config.add_static_view(name='css', path='static/css')
        •   config.add_static_view(name='js', path='static/js')
    •   外部の静的メディアサーバを使う
        •   config.add_static_view(name='http://staticserver.com/',
            path='static')
        •   name 引数の値を設定で切り替えることも
•   pyramid_assetviews というパッケージを使うとトップレベルのファイル URL
    を簡単に設定できる (らしい)

    config.include("pyramid_assetviews")
    config.add_asset_views("static", ["robots.txt", "favicon.ico"])
                                                                      #40
asset spec
•   パッケージ内のファイルを参照する方法
•   パッケージ名と相対パスをコロンで繋げる
    •   例: my.package:static/baz.css
•   テンプレートと静的ファイルのパスを指定する箇所で使用できる




                                       #41
セッション
pyramid_beaker を使うと Pylons と同じように設定できる
•   ini ファイル

    session.type = file
    session.data_dir = %(here)s/data/sessions/data
    session.lock_dir = %(here)s/data/sessions/lock
    session.key = akhet_demo
    session.secret = 0cb243f53ad865a0f70099c0414ffe9cfcfe03ac

•   main 関数

    config.include("pyramid_beaker")




                                                           #42
おわりに
まとめ
•   Pyramid に移行するメリットはある
    •   最新のライブラリへの追従
    •   拡張性
•   既存の Pylons アプリケーションの移植は慎重に考える必要あり
•   移植の戦略
    •   ゼロから Pyramid で書き直す
    •   共存させつつ徐々に移植する




                                        #44
Pyramid の拡張方法
※時間の関係で今回は割愛しました (いずれどこかで発表したい)
•   設定ディレクティブ
•   ビューマッパー
•   リクエストファクトリ
•   イベントシステム
•   tween (Pyramid 内の WSGI ミドルウェアのようなもの)
•   Zope コンポーネントアーキテクチャ (ZCA)




                                           #45
情報源
•   Pylons Project JP http://www.pylonsproject.jp/
•   facebookグループ pylonsproject.jp
•   Pyramid ドキュメント (翻訳)
    http://docs.pylonsproject.jp/projects/pyramid-doc-ja/en/latest/
•   Pylons ユーザのための Pyramid (翻訳)
    http://docs.pylonsproject.jp/projects/pyramid_cookbook-ja
    /en/latest/pylons/index.html




                                                                      #46
Thank you!
ご清聴ありがとうございました

Contenu connexe

Tendances

RedmineとGitとスクラム
RedmineとGitとスクラムRedmineとGitとスクラム
RedmineとGitとスクラムTakashi Okamoto
 
いつやるの?Git入門 v1.1.0
いつやるの?Git入門 v1.1.0いつやるの?Git入門 v1.1.0
いつやるの?Git入門 v1.1.0Masakazu Matsushita
 
Jjug 20140430 gradle_basic
Jjug 20140430 gradle_basicJjug 20140430 gradle_basic
Jjug 20140430 gradle_basicTakuma Watabiki
 
インフラ自動化とHashicorp tools
インフラ自動化とHashicorp toolsインフラ自動化とHashicorp tools
インフラ自動化とHashicorp toolsUchio Kondo
 
.NET Compiler Platform
.NET Compiler Platform.NET Compiler Platform
.NET Compiler Platform信之 岩永
 
FuelPHP活用事例
FuelPHP活用事例FuelPHP活用事例
FuelPHP活用事例Yusuke Naka
 
8時間耐久 PHP構築の教室
8時間耐久 PHP構築の教室8時間耐久 PHP構築の教室
8時間耐久 PHP構築の教室Yusuke Ando
 
PySide/QtWebkitで楽々 slideshare Hack
PySide/QtWebkitで楽々 slideshare HackPySide/QtWebkitで楽々 slideshare Hack
PySide/QtWebkitで楽々 slideshare HackKazushige TAKEUCHI
 
Git flowの活用事例
Git flowの活用事例Git flowの活用事例
Git flowの活用事例Hirohito Kato
 
一人でもはじめるGitでバージョン管理
一人でもはじめるGitでバージョン管理一人でもはじめるGitでバージョン管理
一人でもはじめるGitでバージョン管理Takafumi Yoshida
 
Git & GitHub & kintone でウルトラハッピー!
Git & GitHub & kintone でウルトラハッピー!Git & GitHub & kintone でウルトラハッピー!
Git & GitHub & kintone でウルトラハッピー!ymmt
 
Gitのよく使うコマンド
Gitのよく使うコマンドGitのよく使うコマンド
Gitのよく使うコマンドYUKI Kaoru
 
CircleCIを使ったSpringBoot/GAEアプリ開発の効率化ノウハウ
CircleCIを使ったSpringBoot/GAEアプリ開発の効率化ノウハウCircleCIを使ったSpringBoot/GAEアプリ開発の効率化ノウハウ
CircleCIを使ったSpringBoot/GAEアプリ開発の効率化ノウハウTakeshi Mikami
 
Apache Archiva を試す
Apache Archiva を試すApache Archiva を試す
Apache Archiva を試すbouzuya
 
Metahub for github
Metahub for githubMetahub for github
Metahub for githubSuguru Oho
 
Djangoのエントリポイントとアプリケーションの仕組み
Djangoのエントリポイントとアプリケーションの仕組みDjangoのエントリポイントとアプリケーションの仕組み
Djangoのエントリポイントとアプリケーションの仕組みShinya Okano
 
ホームディレクトリに埋もれた便利なコードをさがせ!
ホームディレクトリに埋もれた便利なコードをさがせ!ホームディレクトリに埋もれた便利なコードをさがせ!
ホームディレクトリに埋もれた便利なコードをさがせ!Yohei Fushii
 

Tendances (20)

RedmineとGitとスクラム
RedmineとGitとスクラムRedmineとGitとスクラム
RedmineとGitとスクラム
 
いつやるの?Git入門 v1.1.0
いつやるの?Git入門 v1.1.0いつやるの?Git入門 v1.1.0
いつやるの?Git入門 v1.1.0
 
真Drone入門
真Drone入門真Drone入門
真Drone入門
 
Jjug 20140430 gradle_basic
Jjug 20140430 gradle_basicJjug 20140430 gradle_basic
Jjug 20140430 gradle_basic
 
インフラ自動化とHashicorp tools
インフラ自動化とHashicorp toolsインフラ自動化とHashicorp tools
インフラ自動化とHashicorp tools
 
.NET Compiler Platform
.NET Compiler Platform.NET Compiler Platform
.NET Compiler Platform
 
FuelPHP活用事例
FuelPHP活用事例FuelPHP活用事例
FuelPHP活用事例
 
8時間耐久 PHP構築の教室
8時間耐久 PHP構築の教室8時間耐久 PHP構築の教室
8時間耐久 PHP構築の教室
 
PySide/QtWebkitで楽々 slideshare Hack
PySide/QtWebkitで楽々 slideshare HackPySide/QtWebkitで楽々 slideshare Hack
PySide/QtWebkitで楽々 slideshare Hack
 
Git flowの活用事例
Git flowの活用事例Git flowの活用事例
Git flowの活用事例
 
Jenkins と groovy
Jenkins と groovyJenkins と groovy
Jenkins と groovy
 
一人でもはじめるGitでバージョン管理
一人でもはじめるGitでバージョン管理一人でもはじめるGitでバージョン管理
一人でもはじめるGitでバージョン管理
 
Git & GitHub & kintone でウルトラハッピー!
Git & GitHub & kintone でウルトラハッピー!Git & GitHub & kintone でウルトラハッピー!
Git & GitHub & kintone でウルトラハッピー!
 
Gitのよく使うコマンド
Gitのよく使うコマンドGitのよく使うコマンド
Gitのよく使うコマンド
 
CircleCIを使ったSpringBoot/GAEアプリ開発の効率化ノウハウ
CircleCIを使ったSpringBoot/GAEアプリ開発の効率化ノウハウCircleCIを使ったSpringBoot/GAEアプリ開発の効率化ノウハウ
CircleCIを使ったSpringBoot/GAEアプリ開発の効率化ノウハウ
 
Apache Archiva を試す
Apache Archiva を試すApache Archiva を試す
Apache Archiva を試す
 
Metahub for github
Metahub for githubMetahub for github
Metahub for github
 
Djangoのエントリポイントとアプリケーションの仕組み
Djangoのエントリポイントとアプリケーションの仕組みDjangoのエントリポイントとアプリケーションの仕組み
Djangoのエントリポイントとアプリケーションの仕組み
 
ホームディレクトリに埋もれた便利なコードをさがせ!
ホームディレクトリに埋もれた便利なコードをさがせ!ホームディレクトリに埋もれた便利なコードをさがせ!
ホームディレクトリに埋もれた便利なコードをさがせ!
 
Fcp
FcpFcp
Fcp
 

Similaire à Pylons ユーザのための Pyramid 移行ガイド

What makes pyramid unique
What makes pyramid uniqueWhat makes pyramid unique
What makes pyramid uniqueAtsushi Odagiri
 
「モダンPerl入門」の入門
「モダンPerl入門」の入門「モダンPerl入門」の入門
「モダンPerl入門」の入門Songhee Han
 
Multibranch Pipeline with Docker 入門編
Multibranch Pipeline with Docker 入門編Multibranch Pipeline with Docker 入門編
Multibranch Pipeline with Docker 入門編kimulla
 
Composer による依存管理 と Packagist によるライブラリの公開
Composer による依存管理 と Packagist によるライブラリの公開Composer による依存管理 と Packagist によるライブラリの公開
Composer による依存管理 と Packagist によるライブラリの公開Shogo Kawahara
 
130329 04
130329 04130329 04
130329 04openrtm
 
20130329 rtm4
20130329 rtm420130329 rtm4
20130329 rtm4openrtm
 
appengine ja night #25 Google App Engine for PHP
appengine ja night #25 Google App Engine for PHPappengine ja night #25 Google App Engine for PHP
appengine ja night #25 Google App Engine for PHPRyo Yamasaki
 
Backlogでの Perlのつかいかた
Backlogでの PerlのつかいかたBacklogでの Perlのつかいかた
Backlogでの PerlのつかいかたRyuzo Yamamoto
 
Firefoxの開発プロセス
Firefoxの開発プロセスFirefoxの開発プロセス
Firefoxの開発プロセスMakoto Kato
 
Jjug springセッション
Jjug springセッションJjug springセッション
Jjug springセッションYuichi Hasegawa
 
GitHubのリポジトリ(32個)を 覗いてみよう。 ただし、READMEだけね
GitHubのリポジトリ(32個)を 覗いてみよう。 ただし、READMEだけねGitHubのリポジトリ(32個)を 覗いてみよう。 ただし、READMEだけね
GitHubのリポジトリ(32個)を 覗いてみよう。 ただし、READMEだけねNaoto TAKAHASHI
 
企業におけるSpring@日本springユーザー会20090624
企業におけるSpring@日本springユーザー会20090624企業におけるSpring@日本springユーザー会20090624
企業におけるSpring@日本springユーザー会20090624Yusuke Suzuki
 
Draft: Observability, Service Mesh and Microservices
Draft: Observability, Service Mesh and MicroservicesDraft: Observability, Service Mesh and Microservices
Draft: Observability, Service Mesh and MicroservicesTaiki
 
オープン・ソースで構築するARMマイコン開発環境 ―― GCC,Eclipse,OpenOCDで統合開発環境,JTAGデバッグもできる!
オープン・ソースで構築するARMマイコン開発環境 ―― GCC,Eclipse,OpenOCDで統合開発環境,JTAGデバッグもできる!オープン・ソースで構築するARMマイコン開発環境 ―― GCC,Eclipse,OpenOCDで統合開発環境,JTAGデバッグもできる!
オープン・ソースで構築するARMマイコン開発環境 ―― GCC,Eclipse,OpenOCDで統合開発環境,JTAGデバッグもできる!Masaki Muranaka
 
勉強会 Cvml python基礎
勉強会 Cvml python基礎勉強会 Cvml python基礎
勉強会 Cvml python基礎真哉 杉野
 
密着! nibohsiデプロイ 13:00-13:05 - railsアプリのデプロイ事例 -
密着! nibohsiデプロイ 13:00-13:05 - railsアプリのデプロイ事例 -密着! nibohsiデプロイ 13:00-13:05 - railsアプリのデプロイ事例 -
密着! nibohsiデプロイ 13:00-13:05 - railsアプリのデプロイ事例 -Yukihiko SAWANOBORI
 
はてなにおける継続的デプロイメントの現状と Docker の導入
はてなにおける継続的デプロイメントの現状と Docker の導入はてなにおける継続的デプロイメントの現状と Docker の導入
はてなにおける継続的デプロイメントの現状と Docker の導入Yu Nobuoka
 

Similaire à Pylons ユーザのための Pyramid 移行ガイド (20)

What makes pyramid unique
What makes pyramid uniqueWhat makes pyramid unique
What makes pyramid unique
 
「モダンPerl入門」の入門
「モダンPerl入門」の入門「モダンPerl入門」の入門
「モダンPerl入門」の入門
 
Multibranch Pipeline with Docker 入門編
Multibranch Pipeline with Docker 入門編Multibranch Pipeline with Docker 入門編
Multibranch Pipeline with Docker 入門編
 
Spring3.1概要x di
Spring3.1概要x diSpring3.1概要x di
Spring3.1概要x di
 
Composer による依存管理 と Packagist によるライブラリの公開
Composer による依存管理 と Packagist によるライブラリの公開Composer による依存管理 と Packagist によるライブラリの公開
Composer による依存管理 と Packagist によるライブラリの公開
 
130329 04
130329 04130329 04
130329 04
 
20130329 rtm4
20130329 rtm420130329 rtm4
20130329 rtm4
 
appengine ja night #25 Google App Engine for PHP
appengine ja night #25 Google App Engine for PHPappengine ja night #25 Google App Engine for PHP
appengine ja night #25 Google App Engine for PHP
 
Backlogでの Perlのつかいかた
Backlogでの PerlのつかいかたBacklogでの Perlのつかいかた
Backlogでの Perlのつかいかた
 
ProjectAtomic-and-geard
ProjectAtomic-and-geardProjectAtomic-and-geard
ProjectAtomic-and-geard
 
Firefoxの開発プロセス
Firefoxの開発プロセスFirefoxの開発プロセス
Firefoxの開発プロセス
 
Jjug springセッション
Jjug springセッションJjug springセッション
Jjug springセッション
 
GitHubのリポジトリ(32個)を 覗いてみよう。 ただし、READMEだけね
GitHubのリポジトリ(32個)を 覗いてみよう。 ただし、READMEだけねGitHubのリポジトリ(32個)を 覗いてみよう。 ただし、READMEだけね
GitHubのリポジトリ(32個)を 覗いてみよう。 ただし、READMEだけね
 
企業におけるSpring@日本springユーザー会20090624
企業におけるSpring@日本springユーザー会20090624企業におけるSpring@日本springユーザー会20090624
企業におけるSpring@日本springユーザー会20090624
 
G * magazine 0
G * magazine 0G * magazine 0
G * magazine 0
 
Draft: Observability, Service Mesh and Microservices
Draft: Observability, Service Mesh and MicroservicesDraft: Observability, Service Mesh and Microservices
Draft: Observability, Service Mesh and Microservices
 
オープン・ソースで構築するARMマイコン開発環境 ―― GCC,Eclipse,OpenOCDで統合開発環境,JTAGデバッグもできる!
オープン・ソースで構築するARMマイコン開発環境 ―― GCC,Eclipse,OpenOCDで統合開発環境,JTAGデバッグもできる!オープン・ソースで構築するARMマイコン開発環境 ―― GCC,Eclipse,OpenOCDで統合開発環境,JTAGデバッグもできる!
オープン・ソースで構築するARMマイコン開発環境 ―― GCC,Eclipse,OpenOCDで統合開発環境,JTAGデバッグもできる!
 
勉強会 Cvml python基礎
勉強会 Cvml python基礎勉強会 Cvml python基礎
勉強会 Cvml python基礎
 
密着! nibohsiデプロイ 13:00-13:05 - railsアプリのデプロイ事例 -
密着! nibohsiデプロイ 13:00-13:05 - railsアプリのデプロイ事例 -密着! nibohsiデプロイ 13:00-13:05 - railsアプリのデプロイ事例 -
密着! nibohsiデプロイ 13:00-13:05 - railsアプリのデプロイ事例 -
 
はてなにおける継続的デプロイメントの現状と Docker の導入
はてなにおける継続的デプロイメントの現状と Docker の導入はてなにおける継続的デプロイメントの現状と Docker の導入
はてなにおける継続的デプロイメントの現状と Docker の導入
 

Pylons ユーザのための Pyramid 移行ガイド

  • 1. Pylons ユーザのための Pyramid 移行ガイド Author: Nozomu Kaneko Django & Pyramid Con JP 2012 Publication: (in conjunction with Pycon JP 2012) Date: 2012-09-16 #1
  • 2. お前誰よ • 金子望 (Twitter: @knzm2011) • 勤務先: トライアックス株式会社 • Pylons を使い始めて5年ぐらい • Python 歴もだいたい同じくらい • 入社後に Python を使い始めて、気がついたら CMS フレームワークを作ってた • 翻訳とか • Python ドキュメント日本語翻訳プロジェクト • PEP 333: Python Web Server Gateway Interface v1.0 • Pylons 0.9.7 ドキュメント • Pylons プロジェクトドキュメント / Pyramid ドキュメント • Pylons Project JP の中の人 • 実は Pyramid はあまり使ってない (これから...) #2
  • 3. アジェンダ • 第1部 Pyramid の概要 • Pyramid FAQ • Pylons に何が起こったか • Pylons 1.x の何が悪かったのか • Pyramid の特徴 • 第2部 移植の方針 • Pylons 1.0 プロジェクトを Pyramid に移植すべき3つの理由 • Pylons 1.0 プロジェクトを Pyramid に移植すべきでない理由 • 移植方法 • 移植における注意点 • 移植デモ • 第3部 Pylons と Pyramid の比較 #3
  • 5. Pyramid FAQ • Pyramid と Pylons の関係は? • repoze.bfg と Pylons 1.0 という 2 つのウェブフレームワークが合流して Pyramid ができた • コードベースは repoze.bfg • Pyramid を開発しているプロジェクトの名前は Pylons プロジェクト • ウェブフレームワークとしての Pylons もまだ現役 • repoze.bfg とは何ですか? • repoze = Zope 由来のコンポーネントを WSGI アプリケーションで利用 できるようにしたコンポーネント集 • repoze.bfg = repoze のコンポーネントを再構成したフレームワーク #5
  • 6. Pyramid FAQ • Python 3 で動きますか? • Pyramid 1.3 から Python 3.2 以上で動きます (thanks to @aodag) • Pylons 1.0 プロジェクトを Pyramid に移植すべきですか? • 後で詳しく説明します #6
  • 7. Pylons に何が起こったか • Pylons 1.0 までは順調に開発が進んだ • 特に、周辺ライブラリも含めた Pylons スタックは WSGI ベースの フレームワークとして代表的な存在に • 拡張性を取り入れるために改良しようとして、問題があることが分かった • Ben Bangert による blog 記事 (2010年11月): “Why Extending Through Subclassing (a framework’s classes) is a Bad Idea” • Pylons 2 の開発を進めるうちに repoze.bfg と似てきたことで、コードベース と開発コミュニティをマージする方向へとシフト • 2011年1月31日 Pyramid 1.0 リリース #7
  • 8. Pylons 1.x の何が悪かったのか • サブクラス化によるフレームワークの拡張 • フレームワークを改良したくても、すべての主要なメソッドは事実上の API として凍結されていた • Pylons では、フレームワークの提供するベースクラスをサブクラス化 することでプロジェクトを作成 (BaseController, BaseWSGIApp) • ユーザはカスタマイズが必要なメソッドを自由にオーバーライドする • 深い継承によるパフォーマンスの問題 • フレームワーク内の親クラスに依存するため単体テストしづらい • 多重継承による奇妙な衝突が発生 • StackedObjectProxy の使用 • スレッドローカルかつアプリケーション固有のグローバル変数 • 時に混乱の原因となる #8
  • 9. Pyramid の特徴 • Pylons と repoze.bfg それぞれに由来する豊富な機能 (※) • ルーティング: URLディスパッチ or トラバーサル • データベースエンジン: SQLAlchemy or ZODB • テンプレートエンジン: Mako or Chameleon • scaffold • インタラクティブデバッガー • セキュリティ (Authentication / Authorization) • これまでのフレームワーク開発で得られた教訓 • 徹底したテストコード • 徹底したドキュメンテーション (API と実装の分離) • サブクラス化に頼らない拡張方法 • グローバル変数の排除 ※ フレームワークが乱立することを防ぐため、 Pyramid ではフレームワーク内で #9 ある程度の機能の重複があることは想定内とされている
  • 11. Pylons 1.0 プロジェクトを Pyramid に移植すべき 3つの理由 • Pylons 1.0.x は「レガシー」扱いで今後はメンテナンスのみ • 今後 Pylons に大きな機能追加はない • Pyramid の方が (色々な意味で) 強力 • 特に拡張性が非常に高いのでメタフレームワークとして魅力的 • Pyramid for Pylons Users が公開された (2012-06-12) • 翻訳済み: http://docs.pylonsproject.jp /projects/pyramid_cookbook-ja/en/latest/pylons/index.html #11
  • 12. Pylons 1.0 プロジェクトを Pyramid に移植すべきでない 理由 • Pyramid 自体が開発途上 • 例) 1.3.2 で Mako テンプレートの継承ができなくなる致命的なバグがあり、 修正版 (1.3.3) がリリースされるまで 3 ヶ月近くまともに使えなかった • 全体的に Pylons ユーザ向けの機能はまだ弱い • 情報が少ない • Pylons (特に大規模プロジェクト) からの移行に関してはあまり情報がない • 日本では Pyramid ユーザ自体が少ない (~30人ぐらい?) • Pylons Project JP http://www.pylonsproject.jp/ にぜひ参加を (宣伝) #12
  • 13. 移植する? しない? • 注: 個人の感想です • Pyramid と Pylons は内部がかなり違うので、移植はそれなりに覚悟が必要 • まずは新規のプロジェクトで Pyramid を試してみる • うまくいったら小規模なプロジェクトに適用 • 既存のプロジェクトを移植する場合は、今のうちから計画的に準備しておくといいかも • テストを書く • コントローラを軽くする • コントローラ以外の場所ではグローバル変数 pylons.request を使わない • Python 2.6 or 2.7 に環境をアップデートする • etc. #13
  • 14. 移植の戦略 (1) ゼロから Pyramid で書き直す • あまり変更せずに再利用可能 • モデル, テンプレート, 静的ファイル • 変更が必要 • コントローラ, ルーティング, グローバル変数 #14
  • 15. 移植の戦略 (1) ゼロから Pyramid で書き直す BeforeRender イベントを使ってレンダラーグローバル変数を追加する例: from pyramid.events import subscriber from pyramid.events import BeforeRender from pyramid.threadlocal import get_current_request from mypylonsproject.lib import helpers @subscriber(BeforeRender) def add_renderer_globals(event):    event["h"] = helpers    request = event.get("request")    if request is None:        request = get_current_request()    event["c"] = request.tmpl_context #15
  • 16. 移植の戦略 (2) 共存させつつ徐々に移植する • 一度に 1 つずつ URL を移植する • 移植された URL -> Pyramid • 移植されていない URL -> Pylons • 選択肢 • A) mod_rewrite を使用する • Pyramid と Pylons の両方のアプリケーションが別プロセスで実行される • B) INI ファイルの中で paste.cascade を設定する • 最初に片方のアプリケーションを実行してみて、 “Not Found” が 返る場合にはもう片方のアプリケーションを試す • Pylons が静的 ファイルを返すのと同じ方法 • C) Pyramid のビューで Pylons アプリケーションをラップする #16
  • 17. 移植の戦略 (2) 共存させつつ徐々に移植する NotFound ビューを使用する例: from mypylonsproject import thepylonsapp class LegacyView(object):    def __init__(self, app):        self.app = app    def __call__(self, request):        return request.get_response(self.app) if __name__ == '__main__':   legacy_view = LegacyView(thepylonsapp)   config = Configurator()   config.add_notfound_view(legacy_view)   # ... rest of config ... #17
  • 18. 移植における注意点 • 複数のアプリケーションを同時に実行する際に調整が必要な箇所 • データベース接続数、セッションの共有、ファイルのロックなど • Pyramid アプリケーションを Python 2 と 3 のどちらで書くかは重要 • Python 3 • Pyramid は Python 3 対応済み • Pyramid が必要とするライブラリもほとんどは Python 3 対応している • 一部のライブラリは Python 3 対応していない • PIL, Paste, WebHelpers, FormEncode, Pylons • Python 2 • バージョンは 2.6 以上必須 • Pyramid は Python 2.6 以上で動作 • 多くのライブラリで Python 2.5 以下は徐々にサポートが打ち切られている #18
  • 19. レガシー Pylons アプリケーションで使用しているライブラリのバージョンが 古いと Pyramid との共存が難しい #19
  • 20. 実際にやってみた • Pylons の QuickWiki チュートリアルを Pyramid に移植 • 「ゼロから Pyramid で書き直す」パターン • https://bitbucket.org/knzm/quickwiki-pyramid/wiki/PatchList #20
  • 21. 第3部 Pylons と Pyramid の比較 Pylons 流のアプリケーション開発が、 Pyramid に移行することでどう変わるか
  • 22. paster コマンド -> p* コマンド Pylons Pyramid 備考 paster create pcreate オプション -t => -s paster serve pserve   paster shell pshell 初期設定される変数が違う paster setup-app initialize_App_db App はアプリケーション名 #22
  • 23. scaffold • 新しくプロジェクトを開始する場合 • Pylons: paster create -t <テンプレート名> • Pyramid: pcreate -s <scaffold 名> • 標準 scaffold • alchemy: URL ディスパッチ、 SQLAlchemy • starter: URL ディスパッチ、データベースなし • zodb: トラバーサル、 ZODB #23
  • 25. main 関数 • アプリケーションを返すトップレベルの関数 • Pylons: pylonsapp/config/middleware.py の make_app 関数 • Pyramid: pyramidapp/__init__.py の main 関数 • Pyramid の main 関数は Pylons の middleware.py, environment.py, routing.py の内容を含む • Pyramid では Configurator を使って Pylons よりも簡潔に設定が行える • WSGI ミドルウェアはありません #25
  • 26. main 関数 Pyramid の main 関数の例: from pyramid.config import Configurator def main(global_config, **settings):    """ This function returns a Pyramid WSGI application.    """    config = Configurator(settings=settings)    config.add_static_view('static', 'static', cache_max_age=3600)    config.add_route('home', '/')    config.scan()    return config.make_wsgi_app() #26
  • 27. ルーティングとビュー • Pylons の場合 • ルーティングの登録は map.connect() で行う • map.connect('/article/{id}', controller='article', action='show') • controllers ディレクトリの同名のコントローラが呼ばれる • コントローラは BaseController を継承したクラス • Pyramid の場合 • ルーティングの登録とビューの登録が分かれている • ルーティングの登録 • config.add_route('article_page', 'article/{id}') • ビューの登録 • config.add_view() または @view_config デコレータ • ルーティングは必ず名前を持つ • ビューは任意の callable オブジェクトを指定できる • 関数、クラス、 __call__ メソッドを 実装したインスタンス #27
  • 28. view_config のパラメータ • 述語引数 • route_name • context • request_method • request_param • match_param • custom_predicates • 非述語引数 • renderer • permission • http_cache #28
  • 29. view_config のパラメータ • 述語引数 • route_name - ルート名 • context - 例外ビューなどで使用 • request_method - GET とか POST とか • request_param - key=val • match_param - ルーティングの変数部分 (/{title} とか) • custom_predicates - 任意の関数 • 非述語引数 • renderer • permission • http_cache #29
  • 30. リソース • 「トラバース」というルーティング方式を使う場合に重要な概念 • URL ディスパッチ (Pylons と同様のルーティング方式) を使う場合は、 あまり詳しく知る必要はない • 重要なのは root リソースのみ • 使い方 • ビジネスロジックの定義 • セキュリティ #30
  • 32. 特殊グローバル変数 pylons.request • ビュー関数の中では request 引数 • クラスベースのビューメソッドの中では self.request • テンプレートの中では、 request あるいは req • それ以外の場所では pyramid.threadlocal.get_current_request() を呼び出して request を取得可能 (pshell やユニットテストなどで request オブジェクトが渡ってこない場合) pylons.response • Pyramid にはグローバルなレスポンスオブジェクトはない #32
  • 33. 特殊グローバル変数 pylons.tmpl_context, pylons.c • テンプレートに変数を渡したい場合、ビューから dict を返すとレンダラー経由で テンプレートに渡る • ※過去に request.tmpl_context が導入されたが、後に廃止された #33
  • 34. 特殊グローバル変数 pylons.app_globals • 最も近い等価物は request.registry か request.registry.settings • レジストリはアプリケーションに関する設定を集約するために Pyramid 内部で使用されるオブジェクト • request.registry.settings は通常アプリケーションの設定が格納される #34
  • 35. 特殊グローバル変数 pylons.url (h.url_for) • request が URL 生成のためのメソッドを持っている • request.route_url() • request.static_url() • request.resource_url() #35
  • 36. 特殊グローバル変数 pylons.session • request.session • pyramid_beaker 拡張を有効にするか、 Configurator に session_factory を渡す from pyramid.session import UnencryptedCookieSessionFactoryConfig session_factory = UnencryptedCookieSessionFactoryConfig('secret') config = Configurator(session_factory=session_factory) pylons.cache • Pyramid はキャッシュ機能を内蔵していない • pyramid_beaker 拡張を使用する #36
  • 37. HTTP エラーとリダイレクト Pylons の場合 (コントローラの中で): abort(404)   # Not Found abort(500)   # Internal server error redirect(url("section1"))   # リダイレクト Pyramid の場合 (ビューの中で): raise exc.HTTPNotFound()            # Not Found return exc.HTTPNotFound()           # 戻り値として返すこともできる raise exc.HTTPInernalServerError()  # Internal server error raise exc.HTTPFound(request.route_url("section1"))   # リダイレクト HTTNotFound と HTTPForbidden は Pyramid 内部でも発生する #37
  • 39. 静的ファイル • Pyramid で静的ファイルを返す標準の方法 config.add_static_view('static', 'static', cache_max_age=3600) • 静的 URLにプレフィックス “/static” が付く • トップレベルのファイル URL を返せない • どうするか • favicon.ico <link rel="shortcut icon" href="${request.static_url('pyramidapp:static/favicon.ico')}" /> • robots.txt Alias /robots.txt /var/www/static/norobots.txt #39
  • 40. 静的ファイル • 高度な使い方 • 複数のパスを設定する • config.add_static_view(name='images', path='static/images') • config.add_static_view(name='css', path='static/css') • config.add_static_view(name='js', path='static/js') • 外部の静的メディアサーバを使う • config.add_static_view(name='http://staticserver.com/', path='static') • name 引数の値を設定で切り替えることも • pyramid_assetviews というパッケージを使うとトップレベルのファイル URL を簡単に設定できる (らしい) config.include("pyramid_assetviews") config.add_asset_views("static", ["robots.txt", "favicon.ico"]) #40
  • 41. asset spec • パッケージ内のファイルを参照する方法 • パッケージ名と相対パスをコロンで繋げる • 例: my.package:static/baz.css • テンプレートと静的ファイルのパスを指定する箇所で使用できる #41
  • 42. セッション pyramid_beaker を使うと Pylons と同じように設定できる • ini ファイル session.type = file session.data_dir = %(here)s/data/sessions/data session.lock_dir = %(here)s/data/sessions/lock session.key = akhet_demo session.secret = 0cb243f53ad865a0f70099c0414ffe9cfcfe03ac • main 関数 config.include("pyramid_beaker") #42
  • 44. まとめ • Pyramid に移行するメリットはある • 最新のライブラリへの追従 • 拡張性 • 既存の Pylons アプリケーションの移植は慎重に考える必要あり • 移植の戦略 • ゼロから Pyramid で書き直す • 共存させつつ徐々に移植する #44
  • 45. Pyramid の拡張方法 ※時間の関係で今回は割愛しました (いずれどこかで発表したい) • 設定ディレクティブ • ビューマッパー • リクエストファクトリ • イベントシステム • tween (Pyramid 内の WSGI ミドルウェアのようなもの) • Zope コンポーネントアーキテクチャ (ZCA) #45
  • 46. 情報源 • Pylons Project JP http://www.pylonsproject.jp/ • facebookグループ pylonsproject.jp • Pyramid ドキュメント (翻訳) http://docs.pylonsproject.jp/projects/pyramid-doc-ja/en/latest/ • Pylons ユーザのための Pyramid (翻訳) http://docs.pylonsproject.jp/projects/pyramid_cookbook-ja /en/latest/pylons/index.html #46