1ファイルと1プロジェクトの中間

想定読者

  • pyramidの機能を色々試してみたい人
  • 特に、設定ファイル(development.ini)を要求するコマンドの作成などをしてみたい人

発端

pyramidの機能を試す際に、1ファイルでアプリケーションが作れるのは便利なのだけれど。
pyramidで提供されているpから始まるコマンド群*1を使おうとすると設定ファイルを要求される。

1ファイルアプリは設定ファイルを書かないので、このレイヤーの機能の実験をしようと思った場合には、
1プロジェクト作る必要があってめんどうだった。(ここでいう1プロジェクトとはsetup.pyを持つpython packageのこと)

ちょっとした機能を試してみる度に、setup.pyを定義していくのは面倒なのでどうすれば良いのか考えてみた。

ポイントは

  • 機能の実験の度にsetup.pyを作成するのが面倒くさい
  • それに付随して、管理対象のpackageが増えていくのが邪魔。
  • 少なくともsetup.pyを書くのは1回だけにしたい。

実際の方法

1. てきとうに親となるプロジェクト(experiment)を作成する
2. 親となるプロジェクトのトップレベルのモジュールのディレクトリの中にサブモジュールを切って作業

以下のようなイメージ

.
├── development.ini
├── experiment
│   ├── __init__.py
│   ├── hello
│   │   ├── __init__.py
│   │   └── hello.ini
│   ├── tests.py
│   └── views.py
├── production.ini
├── setup.cfg
└── setup.py

experimentというパッケージを作成して、実験用のモジュールはexperiment以下に作成する。
これで"python setup.py develop"は初回の一回だけで済むようになる。

1ファイルアプリケーションと実際のウェブアプリケーション開発の中間位の大きさの作業をする時この構成でやると良い気がする。

実際の利用方法

proutesを使って見るために、helloというアプリを作ることにする。
設定ファイルはhello.ini(development.iniという名前でも構わないけれど)

1ファイルアプリケーション(http://docs.pylonsproject.org/projects/pyramid_tutorials/en/latest/humans/creatingux/step01/index.html)との違いは

  • 直接httpserverを起動しないこと
  • 設定ファイル(.ini)から設定情報を取得する形式なこと

hello/__init__.py

from wsgiref.simple_server import make_server

from pyramid.config import Configurator
from pyramid.response import Response

# This acts as the view function
def hello_world(request):
    return Response('hello!')

def main(global_config, **settings):
    # Grab the config, add a view, and make a WSGI app
    config = Configurator(settings=settings)

    config.add_route("index", "/")
    config.add_view(hello_world, route_name="index")
    app = config.make_wsgi_app()
    return app

hello/hello.ini

[app:main]
paste.app_factory = experiment.hello:main

[server:main]
use = egg:waitress
port = 6543
host = 0.0.0.0
起動
# pwd => experiment/hello
$ pserve hello.ini
# Starting server in PID 12014.
# serving on http://0.0.0.0:6543

proutesを使ってみる。

$ proutes hello.ini
Name            Pattern                        View                     
----            -------                        ----                     
index           /                              <function hello_world at 0x1de28c0>

動いている。

注意

  • ここに書かれるものは基本的に使い捨て
  • 実際のwebアプリケーションとか作るのは止めましょう。

*1:pserve,pviews,proutes,pshell,..など

template lookup errorを見つけるスクリプト

pyramidは、template lookup errorが起き得る状態の設定でもアプリケーションが動作する。
もちろん、動的言語的な性質を考えるとruntime errorというのが正しいという気もしないではないけれど。

デプロイ前後にtemplate lookup errorが発生することが分かると結構へこむ。
直接アクセスして調べるのも手間がかかるので事前に検知したい。

use introspector

pyramid.1.3からintrospectionの機能が強化されている。http://pyramid.readthedocs.org/en/latest/narr/introspector.html
内部的には、add_viewやadd_routeなどで、config.action()が呼ばれる際に登録される。

introspectorは以下のようにして取得することが可能

request.registry.introspector

registryへの参照さえあればconfigからでもrequestからでもアクセスできる。

introspectorは

  • viewの情報
  • templateの情報
  • tweenの情報

など色々な情報を持っている。

introspectorの持っているtemplateの情報を利用することでtemplate lookup errorを検知することができるかもしれない。

実際にやってみた。

以下のようなスクリプトを書けば良かった。

from pyramid.paster import bootstrap
import sys

def detect(renderer):
    return renderer.lookup.get_template(renderer.path)

config_uri = sys.argv[1]
env = bootstrap(config_uri)
registry = env["registry"]


for t in registry.introspector.get_category("templates"):
    try:
        renderer = t["introspectable"]["renderer"]
        if renderer.type == ".mako":
            detect(renderer.renderer)
    except Exception as e:
        print str(e)

bootstrapは"develop.ini"や"production.ini"などの設定ファイルのパスを引数として取り、
渡された設定ファイルで立ち上がったアプリケーション(あるいは環境)を返す関数。
viewなどが登録された状態で返ってくるので、これを利用する。

動作チェック

hello0.mako,hello1.makoは存在していないテンプレート。

$ python detect.py development.ini 
Cant locate template for uri 'hello1.mako'
Cant locate template for uri 'hello0.mako'
Cant locate template for uri 'hello1.mako'
Cant locate template for uri 'hello0.mako'

上のスクリプトは、対応しているのがmakoのテンプレートだけだけれど。
真面目にやればtemplate lookup errorを事前にチェックすることができそう。

動作確認に使ったファイルたち


これらは単独では動かない。今日の別記事を参考に
http://d.hatena.ne.jp/podhmo/20120908/1347106193