読者です 読者をやめる 読者になる 読者になる

1ファイルのアプリでview_configを使う。

python pyramid

誰も特をしないpyramidの話。

pyramidのconfiguration

pyramidにはすごく雑にいうと以下の2つのconfigurationの方法がある。

  • declarative configuration
  • imperative configuration

すごく雑に言えば、declarative configurationはデコレーターを使った設定(内部でvenusianが使われる)。imperative configurationはConfiguratorオブジェクトのdirectiveを直接使って設定する方。viewの設定に限って言えば、前者が view_config の利用。後者が add_view の利用。

通常1ファイルではadd_viewを使う

ドキュメントにもある通り通常は、1ファイルのアプリを作るときにはadd_viewを使うことが多い。

from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response


def hello_world(request):
    return Response('Hello %(name)s!' % request.matchdict)

if __name__ == '__main__':
    config = Configurator()
    config.add_route('hello', '/hello/{name}')
    config.add_view(hello_world, route_name='hello')
    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()

1ファイルでも view_config を使いたい

view_configを使いたい。やっぱりpathの設定とview関数が遠い状態は面倒くさい。どうにかできないかと試行錯誤した結果出来るようになった。以下の様にすると1ファイルでview_configが使える

from wsgiref.simple_server import make_server
from pyramid.view import view_config
from pyramid.config import Configurator
from pyramid.response import Response


@view_config(route_name="hello")
def hello_world(request):
    return Response('Hello %(name)s!' % request.matchdict)


if __name__ == '__main__':
    config = Configurator()
    config.add_route('hello', '/hello/{name}')
    config.scan(__name__)
    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()

詳しく説明すると、それこそpyramidの内部のことまでふれなければいけないので省略するけれど。declarative configurationはconfig.scan()の部分により、Configuratorのactionの実行に代わる。venusianというライブラリが使われていて、moduleの開始地点から登録されたclosure(たしか。ココは記憶で書いている)を実行する。そしてこの登録時の名前空間には呼び出されたタイミングでのモジュール名が使われる。

ここでpythonの話になる。例えば上のファイルの名前が app.py だった時に、 python app.py として実行された場合には、つまりエントリーポイントのモジュールの名前は"__main__" として実行される。モジュール名自体は __name__ に格納されている。そんなわけで、__name__ をconfig.scanに渡している。

おまけ

ちょっと試して終了みたいなアプリの時に、一度だけrequestを受け取れば十分みたいなときがある。そのような場合にはserve_foreverを使うよりhandle_requestを使ったほうが楽。

server = make_server('0.0.0.0', 8080, app)
server.handle_request()

すると、1回だけrequestを捌いたら終了してくれる

おまけ2

こういう1ファイルの時にrouteとpathの指定が面倒と言うことがあるその場合には以下の様なdirectiveないしはdecoratorを作ってあげると良いかもしれない。

import venusian
from pyramid.config import PHASE1_CONFIG


def add_simple_view(config, view, path, *args, **kwargs):
    def callback():
        route_name = view.__qualname__
        config.add_route(route_name, path)
        config.add_view(view, route_name=route_name, *args, **kwargs)
    discriminator = ('add_simple_view', path)
    config.action(discriminator, callback, order=PHASE1_CONFIG)


# venusian対応
class simple_view(object):
    def __init__(self, path, *args, **kwargs):
        self.path = path
        self.args = args
        self.kwargs = kwargs

    def register(self, scanner, name, wrapped):
        scanner.config.add_simple_view(wrapped, self.path, *self.args, **self.kwargs)

    def __call__(self, wrapped):
        venusian.attach(wrapped, self.register)
        return wrapped

def includeme(config):
    config.add_directive("add_simple_view", add_simple_view)

これを使うといよいよflaskっぽくなる。

from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
from my import simple_view


@simple_view("/hello/{name}")
def hello_world(request):
    return Response('Hello %(name)s!' % request.matchdict)


if __name__ == '__main__':
    config = Configurator()
    config.include("my")  # ここで上のadd_simple_viewなどが使えるようになる
    config.scan(__name__)
    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()

参考