フックの使用

Pyramid フレームワークの振る舞いに影響を及ぼすために、様々な方法で 「フック」を使用することができます。

Not Found ビューの変更

Pyramid は URL をビューコードにマッピングすることができない場合 not found view を起動します。それは view callable です。 デフォルトの not found ビューが存在します。デフォルトの not found ビュー は、アプリケーション設定によってオーバーライドすることができます。

アプリケーションが imperative configuration を使用している場合、 pyramid.config.Configurator.add_notfound_view() メソッドを使って Not Found ビューを置き換えることができます:

1
2
from helloworld.views import notfound
config.add_notfound_view(notfound)

helloworld.views.notfound を、 Not Found ビューとして使用したい view callable への参照に置き換えてください。 not found view callable は、他と同様のビュー callable です。

一方、アプリケーションが pyramid.view.view_config デコレータと scan を使用している場合、 pyramid.view.notfound_view_config デコレータを使うことで Not Found ビューを交換することができます:

1
2
3
4
5
6
7
8
9
from pyramid.view import notfound_view_config

@notfound_view_config()
def notfound(request):
    return Response('Not Found, dude', status='404 Not Found')

def main(globals, **settings):
   config = Configurator()
   config.scan()

これは、上記の命令型 (imperative) の例で示したのとまったく同じことを 行います。

アプリケーションは、必要なら 複数の not found ビューを定義することができます。 pyramid.config.Configurator.add_notfound_view()pyramid.view.notfound_view_config の両方は、それぞれ pyramid.config.Configurator.add_viewpyramid.view.view_config とほぼ同じ引数を受け取ります。 これが意味するのは、 not found ビューには適用を制限するための述語を持た せることができるということです。例えば:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from pyramid.view import notfound_view_config

@notfound_view_config(request_method='GET')
def notfound_get(request):
    return Response('Not Found during GET, dude', status='404 Not Found')

@notfound_view_config(request_method='POST')
def notfound_post(request):
    return Response('Not Found during POST, dude', status='404 Not Found')

def main(globals, **settings):
   config = Configurator()
   config.scan()

ビューが見つからず、リクエストメソッドが GET だった場合、 notfound_get ビューが呼ばれるでしょう。ビューが見つからず、リクエスト メソッドが POST だった場合、 notfound_post ビューが呼ばれるでしょう。

他のビューのように、 notfound ビューは少なくとも request パラメータ、 あるいは context および request の両方を受け取らなければなりません。 request は拒否されたアクションを表わす現在の request です。 (呼び出し署名の中で使用されていれば) context はビューを呼び出した HTTPNotFound 例外のインスタンスになるでしょう。

pyramid.config.Configurator.add_notfound_view()pyramid.view.notfound_view_config の両方は、リクエストを スラッシュが追加された route に自動的にリダイレクトするために使用する ことができます。 例については Redirecting to Slash-Appended Routes を参照してください。

これは、最小の NotFound ビュー callable を実装するサンプルコードです:

1
2
3
4
from pyramid.httpexceptions import HTTPNotFound

def notfound(request):
    return HTTPNotFound()

Note

NotFound ビュー callable が起動される場合、 request が渡されます。 リクエストの exception 属性は、 not found ビューが呼び出される原因と なった HTTPNotFound 例外のインスタンスに なります。 request.exception.message の値は、なぜ not found エラーが 生じたのかを説明する値になります。 pyramid.debug_notfound 環境設定が true の場合と false の場合で、このメッセージは異なるでしょう。

Note

pyramid.config.Configurator.add_notfound_view()pyramid.view.notfound_view_config の両方は Pyramid 1.3 から 新しく追加されました。古い Pyramid ドキュメンテーションでは、代わりに contextHTTPNotFound として add_view を使うような指示が ありました。これは今でも動作します; 簡便なメソッドとデコレータは、 この機能に対する単なるラッパーです。

Warning

NotFound ビュー callable が ビュー callable の引数/呼び出し規約を変更する に記述されているような 引数リストを受け取った時、ビュー callable への最初の引数として渡される contextHTTPNotFound 例外 インスタンスになります。もしリソースコンテキストがあれば、それは 依然として request.context として参照することができます。

Forbidden View の変更

Pyramid は、使用中の authorization policy に基づいてビュー の実行を認可することができない場合に forbidden view を起動します。 デフォルトの fobidden レスポンスは 403 ステータスコードを持ち、非常に 簡素です。しかし、必要に応じて forbidden レスポンスを生成するビューを オーバーライドすることができます。

forbidden view callable も他のものと同じようにビュー callable の一つです。それを “forbidden” ビューにさせる view configurationpyramid.config.Configurator.add_forbidden_view() API または pyramid.view.forbidden_view_config デコレータの使用から なります。

例えば、 forbidden ビューを登録する pyramid.config.Configurator.add_forbidden_view() メソッドを使用して forbidden ビューを追加することができます:

1
2
3
from helloworld.views import forbidden_view
from pyramid.httpexceptions import HTTPForbidden
config.add_forbidden_view(forbidden_view)

helloworld.views.forbidden_view を、 Forbidden ビューを表わすために 使用したい Python view callable への参照に置き換えてください。

あるいはデコレータと scan を使用したければ、ビュー callable を forbidden ビューとしてマークするために pyramid.view.forbidden_view_config デコレータを使用することが できます:

1
2
3
4
5
6
7
8
9
from pyramid.view import forbidden_view_config

@forbidden_view_config()
def forbidden(request):
    return Response('forbidden')

def main(globals, **settings):
   config = Configurator()
   config.scan()

他のビューと同じように、 forbidden ビューは少なくとも request パラメータ、あるいは context および request の両方を受け取らなければ なりません。 forbidden ビュー callable が contextrequest の両方を受け取る場合、 HTTP 例外がコンテキストとして渡されます。 (通常期待するような) ルーター (router) によって見つかった context は、 ビューが拒否された時には request.context として利用可能です。 request は拒否されたアクションを表わす現在の request です。

これは、最小の forbidden ビューを実装するサンプルコードです:

1
2
3
4
5
from pyramid.view import view_config
from pyramid.response import Response

def forbidden_view(request):
    return Response('forbidden')

Note

forbidden ビュー callable が起動される場合、 request が渡されます。 request の exception 属性は forbidden ビューを呼び出した HTTPForbidden 例外のインスタンスです。 request.exception.message の値は、forbidden が発生した理由を 説明する値になります。また request.exception.result は forbidden 例外に関する拡張情報になります。 pyramid.debug_authorization 環境設定が true の場合と false の場合で、 これらのメッセージは異なるでしょう。

リクエストファクトリの変更

PyramidWSGI サーバからのリクエストを扱う場合は常に、 渡された WSGI 環境に基づいて request オブジェクトが生成されます。 デフォルトでは、リクエストオブジェクトを表わすために pyramid.request.Request クラスのインスタンスが生成されます。

リクエストオブジェクトのインスタンスを生成するために Pyramid が 使用するクラス (別名「ファクトリ」) は、 configurator の コンストラクタに request_factory 引数を渡すことで変更できます。 この引数は、 callable か、または callable を表す dotted Python name のいずれかです。

1
2
3
4
5
6
from pyramid.request import Request

class MyRequest(Request):
    pass

config = Configurator(request_factory=MyRequest)

命令型の設定を行っていて、 configurator を構築した後に 行いたければ、 pyramid.config.Configurator.set_request_factory() メソッドによって登録することもできます:

1
2
3
4
5
6
7
8
from pyramid.config import Configurator
from pyramid.request import Request

class MyRequest(Request):
    pass

config = Configurator()
config.set_request_factory(MyRequest)

Before Render イベントの使用

pyramid.events.BeforeRender イベントの subscriber は、 renderer globals のセットが renderer に渡される前に 検査して修正することができます。イベントオブジェクト自身には、この目的のために 使用することができる辞書風のインタフェースがあります。例えば:

1
2
3
4
5
6
 from pyramid.events import subscriber
 from pyramid.events import BeforeRender

 @subscriber(BeforeRender)
 def add_global(event):
     event['mykey'] = 'foo'

renderer が起動される直前に (ただし set_renderer_globals_factory によって追加されたアプリケーションレベルのレンダラーグローバルファクトリが もしあれば、それらがレンダラグローバル辞書にそれ自身のキーを注入した後で) この型のオブジェクトがイベントとして送られます。

subscriber がレンダラーグローバル辞書に既に存在するキーを追加しようとした 場合、 KeyError が発生します。イベント subscriber は相対順序を 保持しないので、この制限が強制されます。すべての pyramid.events.BeforeRender subscriber およびレンダラーグローバル ファクトリによってレンダラーグローバル辞書に追加されるキーのセットは、 ユニークでなければなりません。

ビューから返された辞書は、 BeforeRender イベントの rendering_val 属性によってアクセスできます。

以下のようにビュー callable から {'mykey': 'somevalue', 'mykey2': 'somevalue2'} を返すことを考えてください:

1
2
3
4
5
from pyramid.view import view_config

@view_config(renderer='some_renderer')
def myview(request):
    return {'mykey': 'somevalue', 'mykey2': 'somevalue2'}

BeforeRender オブジェクトからこれらの値に アクセスするために rendering_val を使うことができます:

1
2
3
4
5
6
7
from pyramid.events import subscriber
from pyramid.events import BeforeRender

@subscriber(BeforeRender)
def read_return(event):
    # {'mykey': 'somevalue'} is returned from the view
    print(event.rendering_val['mykey'])

BeforeRender イベントインタフェースについては pyramid.interfaces.IBeforeRender の API ドキュメンテーションを 参照してください。

レンダラーグローバルに値を追加する際に、イベント subscriber により多くの コントロールを与える別の (非推奨の) メカニズムが レンダラーグローバルの追加 (非推奨) にあります。

レンダラーグローバルの追加 (非推奨)

Warning

この機能は Pyramid 1.1 から非推奨になりました。イベント subscriber が レンダラーグローバルに値を追加できるようにするための推奨される メカニズムは Before Render イベントの使用 の中で文書化されます。

Pyramid がレンダリングを実行するリクエストを扱う場合 (renderer= 設定属性を持つビューが起動された後、あるいは pyramid.renderers モジュール内の render で始まるメソッドの うちのいずれかが呼ばれた場合) は常に、 レンダラーグローバル をレンダラー に送られる システム 値に注入することができます。デフォルトではレンダラー グローバルは注入されず、 (request, context, view, renderer_name のような) 「素の」システム値が、すべてのレンダラーに 渡されるシステム辞書の中にある唯一の値です。

configurator のコンストラクタに renderer_globals_factory 引数を 渡すことにより、レンダラーが起動される度に Pyramid が呼び出す コールバックを追加することができます。このコールバックは、 callable オブジェクト、あるいはそのような callable を表す dotted Python name のいずれかです。

1
2
3
4
5
def renderer_globals_factory(system):
    return {'a': 1}

config = Configurator(
         renderer_globals_factory=renderer_globals_factory)

このようなコールバックは、オリジナルのシステム値を含む単一の位置引数 (名目上 system という名前が付けられます) を受け取らなければなりません。 それは、システム辞書にマージされる値の辞書を返さなければなりません。 システム辞書の中にある値の説明については、 System Values Used During Rendering を参照してください。

命令型の設定を行っていて、 configurator を構築した後に 行いたければ、 pyramid.config.Configurator.set_renderer_globals_factory() メソッドによって登録することもできます:

1
2
3
4
5
6
7
from pyramid.config import Configurator

def renderer_globals_factory(system):
    return {'a': 1}

config = Configurator()
config.set_renderer_globals_factory(renderer_globals_factory)

レスポンスコールバックの使用

他の多くのウェブフレームワークと異なり、 Pyramid は積極的に グローバルなレスポンスオブジェクトを生成しません。 アプリケーションは response callback を追加することによって、 ビューから返されるすべてのレスポンスオブジェクトに対して実行されるアクションを (通常レスポンスを変更するために) 登録できます。

pyramid.request.Request.add_response_callback() メソッドが レスポンスコールバックを登録するために使用されます。

レスポンスコールバックは、 2 つの位置パラメータ requestresponse を受け取る callable です。例えば:

1
2
3
4
5
def cache_callback(request, response):
    """Set the cache_control max_age for the response"""
    if request.exception is not None:
        response.cache_control.max_age = 360
request.add_response_callback(cache_callback)

未処理の例外がアプリケーションコードの中で起こった場合、あるいは view callable によって返されたレスポンスオブジェクトが無効の 場合、レスポンスコールバックは呼ばれません。しかし、 exception view が正常にレンダリングされる場合、レスポンスコールバックは起動 されます: そのような場合、レスポンスコールバックが呼ばれた時の リクエストの request.exception 属性は、デフォルト値 None の 代わりに例外オブジェクトになります。

レスポンスコールバックは、追加された順番で (first-to-most-recently-added; 最後に追加されたものが最初に) 呼ばれます。 NewResponse イベントが送られた 後で 、すべて のレスポンスコールバックが呼ばれます。レスポンスコールバックで発生した エラーは特別な扱いは受けません。それは呼び出し元の Pyramid ルーター アプリケーションに伝搬するでしょう。

レスポンスコールバックは、単一リクエストのライフタイムを持ちます。 すべての リクエストの結果としてレスポンスコールバックが起こることを 期待する場合、新しく作られるすべてのリクエストにコールバックを再登録 しなければなりません (恐らく NewRequest イベントの subscriber 内で)。

finished コールバックの使用

finished callback は、 Pyramid router によって リクエスト処理の最後に無条件で呼ばれる関数です。 finished コールバックは、 リクエストの終わりに無条件でアクションを行なうために使用することができます。

pyramid.request.Request.add_finished_callback() メソッドは、 finished コールバックを登録するために使用されます。

finished コールバックは、単一の位置パラメータ request を受け取る callable です。例えば:

1
2
3
4
5
6
7
8
import logging

log = logging.getLogger(__name__)

def log_callback(request):
    """Log information at the end of request"""
    log.debug('Request is finished.')
request.add_finished_callback(log_callback)

finished コールバックは、追加された順番で (first-to-most-recently-added; 最後に追加されたものが最初に) 呼ばれます。 (response callback と異なり) finished コールバックは、 レスポンスが生成されるのを妨げる例外がアプリケーションコード内で 起こっても常に呼ばれます。

リクエストに関連付けられた finished コールバックのセットは、そのリクエスト を処理する 最終段階で 呼ばれます; それらは、本質的にリクエストが「終わる」 前に router によって呼ばれるまさに最後のものです。レスポンス処理が 既に生じた後、 ルーターのリクエスト処理コード内のトップレベルの finally: ブロックでそれらが呼ばれます。その結果、レスポンス処理は既に終わって いるため、 finished コールバックに渡された request に対して行なった 変更には意味のある効果はありません。また、リクエストのスコープはすべての finished コールバックが処理された後でほとんど直ちに終了します。

finished コールバックで発生したエラーは特別な扱いは受けません。それは 呼び出し元の Pyramid ルーターアプリケーションに伝搬するでしょう。

finished コールバックは、単一リクエストのライフタイムを持ちます。 すべての リクエストの結果として finished コールバックが起こることを 期待する場合、すべての新しく作られるリクエストにコールバックを再登録 しなければなりません (恐らく NewRequest イベントの subscriber 内で)。

トラバーサーの変更

Pyramid が使用するデフォルトの traversal アルゴリズムは The Traversal Algorithm の中で説明されています。必要になることは めったにありませんが、設定によってこのデフォルトのアルゴリズムを異なる トラバーサルパターンと選択的に交換することができます。

1
2
3
4
from pyramid.config import Configurator
from myapp.traversal import Traverser
config = Configurator()
config.add_traverser(Traverser)

上記の例において、 myapp.traversal.Traverser は次のインタフェースを 実装するクラスであると仮定されます:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Traverser(object):
    def __init__(self, root):
        """ Accept the root object returned from the root factory """

    def __call__(self, request):
        """ Return a dictionary with (at least) the keys ``root``,
        ``context``, ``view_name``, ``subpath``, ``traversed``,
        ``virtual_root``, and ``virtual_root_path``.  These values are
        typically the result of a resource tree traversal.  ``root``
        is the physical root object, ``context`` will be a resource
        object, ``view_name`` will be the view name used (a Unicode
        name), ``subpath`` will be a sequence of Unicode names that
        followed the view name but were not traversed, ``traversed``
        will be a sequence of Unicode names that were traversed
        (including the virtual root path, if any) ``virtual_root``
        will be a resource object representing the virtual root (or the
        physical root if traversal was not performed), and
        ``virtual_root_path`` will be a sequence representing the
        virtual root path (a sequence of Unicode names) or None if
        traversal was not performed.

        Extra keys for special purpose functionality can be added as
        necessary.

        All values returned in the dictionary will be made available
        as attributes of the ``request`` object.
        """

複数のトラバーサルアルゴリズムを同時に有効にすることもできます。例えば、 root factory が条件付きで複数の型のオブジェクトを返す場合、 代替トラバーサーアダプタをある特定のクラスあるいはインタフェース 「専用」と宣言することができます。root ファクトリがそのクラスまたは インタフェースを実装したオブジェクトを返した場合、カスタムトラバーサーが 使用されます。そうでなければ、デフォルトトラバーサーが使用されます。例えば:

1
2
3
4
5
from myapp.traversal import Traverser
from myapp.resources import MyRoot
from pyramid.config import Configurator
config = Configurator()
config.add_traverser(Traverser, MyRoot)

もし上記のコード断片が Pyramid __init__.py ファイルの main 関数に 追加されれば、 Pyramid は アプリケーション root factorymyapp.resources.MyRoot オブジェクトのインスタンスを返した時にだけ myapp.traversal.Traverser を使用します。そうでなければ、トラバーサルを 行うためにデフォルトの Pyramid トラバーサーが使用されます。

pyramid.request.Request.resource_url() の URL 生成方法の変更

トラバーサーの変更 で説明されるようなトラバーサーを追加する場合、 pyramid.request.Request.resource_url() API を使用し続けることは 多くの場合有用です。しかし、この API がカスタムトラバーサーに由来する リソースに対して使用された場合、トラバーサルが終了する方法が修正されて いるためデフォルトで生成される URL は正しくないかもしれません。

トラバーサーを追加している場合、 pyramid.config.add_resource_url_adapter() 呼び出しを追加することに よって resource_url() が特定の型の リソースに対する URL を生成する方法を変更できます。

例えば:

1
2
3
4
from myapp.traversal import ResourceURLAdapter
from myapp.resources import MyRoot

config.add_resource_url_adapter(ResourceURLAdapter, MyRoot)

上記の例において、 resource_url に渡された resource がクラス myapp.resources.MyRoot である場合は常に、 resource_url() にサービスを提供するために myapp.traversal.ResourceURLAdapter クラスが使用されます。 resource_iface の引数 MyRoot は、見つかったリソース url ファクトリ に対してリソースによって所有されなければならないインタフェースの型を 表わしています。 resource_iface 引数が省略されれば、このリソース url アダプターはすべてのリソースに使用されます。

IResourceURL を提供するクラスで実装 しなければならない API は以下の通りです:

1
2
3
4
5
6
7
8
9
class MyResourceURL(object):
    """ An adapter which provides the virtual and physical paths of a
        resource
    """
    def __init__(self, resource, request):
        """ Accept the resource and request and set self.physical_path and
        self.virtual_path"""
        self.virtual_path =  some_function_of(resource, request)
        self.physical_path =  some_other_function_of(resource, request)

デフォルトのコンテキスト URL ジェネレータを熟読したければ、 Pylons GitHub Pyramid リポジトリの traversal module に含まれるクラス pyramid.traversal.ResourceURL が利用可能です。

詳細は pyramid.config.add_resource_url_adapter() を参照してください。

Pyramid がビューレスポンスを扱う方法の変更

pyramid.config.Configurator.add_response_adapter() あるいは response_adapter デコレータに関係するフック を使うことにより、 Pyramid がビュー callable を呼び出した結果をどのように 扱うかを型に応じてコントロールすることが可能です。

Note

これは Pyramid 1.1 からの新機能です。

Pyramid は、ビュー callable が返したオブジェクトが「本物の」レスポンス オブジェクトであることを保証するために、様々な場所でビュー callable を 呼び出した結果を IResponse インタフェースに 適合させます。ほとんどの場合、この適合の結果は結果のオブジェクト自体です。 なぜなら、このマニュアルに含まれるドキュメンテーションを読んだ 「一般ユーザ」によって書かれたビュー callable は、常に IResponse インタフェースを実装する オブジェクトを返すからです。最も典型的には、これは pyramid.response.Response クラスあるいはそのサブクラスの インスタンスになるでしょう。一般のユーザが renderer を使用する ように設定されていないビュー callable から非レスポンスオブジェクトを 返す場合、そのユーザは、典型的にはルーターがエラーを発生させることを 期待するでしょう。しかしながら、任意の戻り値から IResponse を実装するオブジェクトに変換する アダプターを提供することにより、ユーザがビュー callable から任意の値を 返せるように Pyramid をフックすることができます。

例えば、ビュー callable が (文字列をレスポンスオブジェクトに変換する renderer を必要とせずに) 裸の文字列オブジェクトを返せるように したければ、文字列をレスポンスに変換するアダプターを登録することができます:

1
2
3
4
5
6
7
8
9
from pyramid.response import Response

def string_response_adapter(s):
    response = Response(s)
    return response

# config is an instance of pyramid.config.Configurator

config.add_response_adapter(string_response_adapter, str)

同様に、ビュー callable から単純化されたレスポンスオブジェクトの一種を 返せるようにしたければ、より複雑な IResponse インタフェースへの アダプターを登録するために IResponse フックを使用することができます:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from pyramid.response import Response

class SimpleResponse(object):
    def __init__(self, body):
        self.body = body

def simple_response_adapter(simple_response):
    response = Response(simple_response.body)
    return response

# config is an instance of pyramid.config.Configurator

config.add_response_adapter(simple_response_adapter, SimpleResponse)

いずれの場合であっても、 pyramid.response.Response オブジェクト を使用する代わりに自分のレスポンスオブジェクトを実装したければ、 pyramid.interfaces.IResponse の中で概説されているすべての 属性とメソッドをそのオブジェクトが実装していることを保証しなければなりません。 また、クラスデコレータとして zope.interface.implementer(IResponse) を使用する必要があります。

1
2
3
4
5
6
7
from pyramid.interfaces import IResponse
from zope.interface import implementer

@implementer(IResponse)
class MyResponse(object):
    # ... an implementation of every method and attribute
    # documented in IResponse should follow ...

代替レスポンスオブジェクトの実装がビュー callable によって返された場合、 そのオブジェクトが (zope.interface.implementer(IResponse) によって) IResponse を実装すると主張するなら、その オブジェクトのためにアダプターを登録する必要はありません; Pyramid は それを直接使用するでしょう。

webob.Response のための IResponse アダプターは、 (pyramid.response.Response とは対照的に) Pyramid によって スタートアップ時にデフォルトで登録されます。なぜなら、このクラスの インスタンス (またこのクラスのサブクラスのインスタンス) はその性質上 IResponse をネイティブに提供するからです。 webob.Response に対して 登録されたアダプターは、単にレスポンスオブジェクトを返します。

pyramid.config.Configurator.add_response_adapter() を使用する代わりに、 pyramid.response.response_adapter デコレータを使用しても構いません:

1
2
3
4
5
6
7
from pyramid.response import Response
from pyramid.response import response_adapter

@response_adapter(str)
def string_response_adapter(s):
    response = Response(s)
    return response

上記の例は、 scan された時に以下と同じ効果があります:

config.add_response_adapter(string_response_adapter, str)

scan によって活性化されるまで、 response_adapter デコレータは効果がないでしょう。

ビューマッパーの変更

ビュー callable のためのデフォルトの呼び出し規則は ビュー 章の中で文書化されます。 view mapper を 使用することで、ユーザがビュー callable を定義する方法を変更できます。

ビューマッパーは、いくつかのキーワード引数を受け取って 1 つの callable を返すオブジェクトです。返された callable は view callable オブジェクトと共に呼び出されます。返された callable は、それ自体 「内部呼び出しプロトコル」 (context, request) で呼び出すことのできる 別の callable を返す必要があります。

複数の方法でビューマッパーを使用することができます:

  • ビュー callable 自体に __view_mapper__ 属性 (それはビューマッパーオブジェクトです) をセットすることによって。
  • マッパーオブジェクトを pyramid.config.Configurator.add_view() (あるいはその宣言的/デコレータ等価物) に mapper 引数として渡す ことによって。
  • デフォルト ビューマッパーを登録することによって。

これは Pylons 「コントローラー」を (ある程度) 模倣するビューマッパーの 一例です。マッパーはいくつかのキーワード引数で初期化されます。その __call__ メソッドはビューオブジェクト (それはクラスになるでしょう) を受け取ります。それは、アクションメソッドとして使用すべき属性を決定す るために、渡される attr キーワード引数を使用します。それが返す ラッパーメソッドは、 (context, request) を受け取り、 matchdict から action をポップした後で matchdict によって示唆された キーワード引数を伴ってアクションメソッドを呼び出した結果を返します。これは、 route マッチ辞書から取り出されたルーティングパラメータをキーワード引数 としてアクションメソッドを呼び出す Pylons スタイルをある程度エミュレート します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# framework

class PylonsControllerViewMapper(object):
    def __init__(self, **kw):
        self.kw = kw

    def __call__(self, view):
        attr = self.kw['attr']
        def wrapper(context, request):
            matchdict = request.matchdict.copy()
            matchdict.pop('action', None)
            inst = view(request)
            meth = getattr(inst, attr)
            return meth(**matchdict)
        return wrapper

class BaseController(object):
    __view_mapper__ = PylonsControllerViewMapper

ユーザは、これらのフレームワークコンポーネントを以下のように使用 するでしょう:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# user application

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

class MyController(BaseController):
    def index(self, id):
        return Response(id)

if __name__ == '__main__':
    config = Configurator()
    config.include(pyramid_handlers)
    config.add_handler('one', '/{id}', MyController, action='index')
    config.add_handler('two', '/{action}/{id}', MyController)
    server.make_server('0.0.0.0', 8080, config.make_wsgi_app())
    server.serve_forever()

pyramid.config.Configurator.set_view_mapper() メソッドは、 (Pyramid 自体によって使用されるスーパーデフォルトビューマッパーをオーバーライド して) デフォルト ビューマッパーをセットするために使用することができます。

単一の ビュー登録は、マッパーを mapper 引数として add_view() へ渡すことによって、 ビューマッパーを使用することができます。

設定デコレータの登録

view_config のようなデコレータは、それがデコレート する関数またはクラスの振る舞いを変更しません。代わりに、 scan が 実行された時に、関数またはクラスの修正版が Pyramid に登録されます。

そのような振る舞いを提供する自分のデコレータが欲しいと思うかもしれません。 これは、 Pyramid が行っているのと同じ方法で Venusian パッケージを使用することにより実現可能です。

例として、関数を登録するデコレータを書きたいと仮定しましょう。その デコレータは、 Pyramid によって提供される application registry の中に Zope Component Architecture 「ユーティリティ」 としてそれがラップする関数を登録します。アプリケーションレジストリおよび レジストリ内部のユーティリティは、アプリケーション設定が少なくとも 部分的に完了してから初めて利用可能になります。設定が開始される前にそれが 実行された場合、通常のデコレータは失敗します。

しかし、 Venusian を使用すれば以下のようにデコレータを書く ことができます:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import venusian
from mypackage.interfaces import IMyUtility

class registerFunction(object):

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

    def register(self, scanner, name, wrapped):
        registry = scanner.config.registry
        registry.getUtility(IMyUtility).register(
            self.path, wrapped)

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

その後、このデコレータを使用してコードのどこでも関数を登録することが できます:

1
2
3
@registerFunction('/some/path')
def my_function():
   do_stuff()

しかし、事前にユーティリティをセットアップできるにもかかわらず、 scan が行なわれた時にだけユーティリティが検索されます:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
from zope.interface import implementer

from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from mypackage.interfaces import IMyUtility

@implementer(IMyUtility)
class UtilityImplementation:

    def __init__(self):
       self.registrations = {}

    def register(self, path, callable_):
       self.registrations[path] = callable_

if __name__ == '__main__':
    config = Configurator()
    config.registry.registerUtility(UtilityImplementation())
    config.scan()
    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()

完全な詳細については Venusian documentation を読んでください。

“tween” の登録

Note

tween は Pyramid 1.2 から追加された機能です。旧バージョンでは利用 できません。

tween (単語 “between” の短縮形) は、 Pyramid ルーターのメイン リクエスト処理関数と、 Pyramid を “app” として使用する上流の WSGI コンポーネントの間に存在するコードです。この機能は Pyramid フレームワーク 拡張によって使われることがあります。例えば、上流の WSGI アプリケーション に返される前に例外を検証するような Pyramid 専用の view timing サポートの ための bookkeeping コードを提供することができます。 tween は WSGI ミドルウェアと多少似た動作をしますが、 Pyramid のレンダリング機構と同様 Pyramid の application registry にアクセスできるコンテキスト内で 動作するという利点があります。

tween ファクトリの作成

tweens を利用するためには「tween ファクトリ」を構築する必要があります。 tween ファクトリは、 2 つの引数 handlerregistry を受け取る グローバルにインポート可能な callable です。 handler はメインの Pyramid リクエスト処理関数か、あるいは別の tween のいずれかです。 registry はこの Configurator によって表わされる Pyramid application registry です。 tween ファクトリは 呼び出された時に tween を返さなければなりません。

tween は、 request オブジェクトを受け取って、 response オブジェクトを返す callable です。

これは tween ファクトリの例です:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 # in a module named myapp.tweens

 import time
 from pyramid.settings import asbool
 import logging

 log = logging.getLogger(__name__)

 def timing_tween_factory(handler, registry):
     if asbool(registry.settings.get('do_timing')):
         # if timing support is enabled, return a wrapper
         def timing_tween(request):
             start = time.time()
             try:
                 response = handler(request)
             finally:
                 end = time.time()
                 log.debug('The request took %s seconds' %
                           (end - start))
             return response
         return timing_tween
     # if timing support is not enabled, return the original
     # handler
     return handler

覚えているかもしれませんが、 tween は request オブジェクトを 受け取って response を返すオブジェクトです。 tween に対する request 引数は、 Pyramid ルーターが WSGI リクエストを受け取った時 に生成されたリクエストです。レスポンスオブジェクトは下流の Pyramid アプリケーションによって生成され、 tween によって返されます。

上記の例で tween ファクトリは timing_tween tween を定義していて、 asbool(registry.settings.get('do_timing')) が true の場合はそれを 返します。そうでなければ渡されたハンドラを単に返します。 registry.settings 属性は、ユーザによって提供される (通常 .ini ファイル中の) 設定へのハンドルです。この場合、ユーザが do_timing 設定を定義していてその設定値が True なら、ユーザは timing を行いたいと 言ったことになり、したがって tween ファクトリは timing tween を返します; そうでなければ timing は一切行わず、単に提供されたハンドラを返します。

この例 の timing tween は、単に開始時刻を記録し、下流のハンドラを呼び、 下流のハンドラによって消費された秒数を記録し、レスポンスを返します。

暗黙的な tween ファクトリの登録

tween ファクトリを作成したら、 pyramid.config.Configurator.add_tween() メソッドを使用して、 その dotted Python name を用いてそれを暗黙の tween チェインに 登録することができます。

これは、 Pyramid アプリケーションで tween ファクトリを「暗黙の」 tween として登録する例です:

1
2
3
 from pyramid.config import Configurator
 config = Configurator()
 config.add_tween('myapp.tweens.timing_tween_factory')

pyramid.config.Configurator.add_tween() への最初の引数として dotted Python name を使用する必要があることに注意してください; これは tween ファクトリを指していなければなりません。 tween ファクトリ オブジェクト自体をメソッドへ渡すことはできません: それはグローバルに インポート可能なオブジェクトを指す dotted Python name である必要が あります。上記の例では、 timing_tween_factory tween ファクトリが myapp.tweens という名前のモジュールに定義されていると仮定されます。 したがって、 tween ファクトリは myapp.tweens.timing_tween_factory のようにしてインポート可能です。

pyramid.config.Configurator.add_tween() を使用する場合、 あなたはシステムに対して、設定で明示的な tween リストが提供されない限り この tween ファクトリをスタートアップ時に使うように、と伝えています。 これが「暗黙の」 tween の意味することです。ユーザはいつでも、暗黙に追加 された tween を並び替えたり除外したりして、明示的な tween リストを提供 することを選択できます。 tween の明示的な順序についての詳細は 明示的な tween 順序 を参照してください。

1つのアプリケーション設定中で pyramid.config.Configurator.add_tween() への呼び出しが複数回 行われた場合、それらの tween はアプリケーションのスタートアップ時に まとめてチェインされます。 add_tween によって追加された 最初の tween ファクトリが Pyramid 例外ビュー tween ファクトリをその handler 引数として呼び出され、次にその直後に追加された tween ファクトリが最初の tween ファクトリの結果をその handler 引数として 呼び出されます。このようにして、すべての tween ファクトリが呼び出される まで際限なく続きます。 Pyramid ルーターは、このチェインによって生成された 最も外側の tween (最後に追加された tween ファクトリによって生成された tween) をそのリクエストハンドラ関数として使用します。例えば:

1
2
3
4
5
 from pyramid.config import Configurator

 config = Configurator()
 config.add_tween('myapp.tween_factory1')
 config.add_tween('myapp.tween_factory2')

上記の例は、以下に見るような暗黙の tween チェインを生成するでしょう:

INGRESS (implicit)
myapp.tween_factory2
myapp.tween_factory1
pyramid.tweens.excview_tween_factory (implicit)
MAIN (implicit)

暗黙的な tween 順序の指示

上述のように、デフォルトではチェインの順序は pyramid.config.Configurator.add_tween() の呼び出しの相対的な 順番によって完全にコントロールされます。しかし、 add_tween の呼び出し元は add_tween()under または over (あるいはその両方) の引数を渡すことで、暗黙の tween チェインの 順序に影響を及ぼすためのオプションのヒントを提供することができます。 これらのヒントは、明示的な tween 順序が使用されていない場合にだけ使用されます。 明示的な tween 順序を設定する方法の説明については、 明示的な tween 順序 を参照してください。

underover (またはその両方) に対して可能な値は:

  • None (デフォルト)
  • tween ファクトリに対する dotted Python name: 同じ設定セッション 内で add_tween 呼び出しで追加された tween ファクトリの predicted dotted name を表わす文字列。
  • 定数 pyramid.tweens.MAIN, pyramid.tweens.INGRESS あるいは pyramid.tweens.EXCVIEW のうちの1つ。
  • 上記のものの任意の組み合わせによる iterable 。これは、期待する tween が含まれていない場合や、他の複数の tween との互換性のために、ユーザが fallback を指定することを可能にします。

実質的に、 under は「メインの Pyramid アプリケーションにより近い」 ことを意味して、 over は「リクエストの入口により近い」ことを意味します。

例えば、以下の add_tween() に対する 呼び出しは、 myapp.tween_factory で表わされる tween ファクトリを (ptweens の順で) メインの Pyramid リクエストハンドラの直接「上」に 置こうとします。

1
2
3
import pyramid.tweens

config.add_tween('myapp.tween_factory', over=pyramid.tweens.MAIN)

上記の例は、以下に見るような暗黙の tween チェインを生成するでしょう:

INGRESS (implicit)
pyramid.tweens.excview_tween_factory (implicit)
myapp.tween_factory
MAIN (implicit)

同様に、以下の add_tween() に対する 呼び出しでは、この tween ファクトリをメインハンドラより「上」で、別に追加 された tween ファクトリより「下」に置こうとします:

1
2
3
4
5
6
7
import pyramid.tweens

config.add_tween('myapp.tween_factory1',
                 over=pyramid.tweens.MAIN)
config.add_tween('myapp.tween_factory2',
                 over=pyramid.tweens.MAIN,
                 under='myapp.tween_factory1')

上記の例は、以下に見るような暗黙の tween チェインを生成するでしょう:

INGRESS (implicit)
pyramid.tweens.excview_tween_factory (implicit)
myapp.tween_factory1
myapp.tween_factory2
MAIN (implicit)

overunder も指定しないことは、 under=INGRESS を指定した ことと等価です。

under (あるいは over) に対するすべてのオプションが現在の設定で 見つからない場合、それはエラーです。あるオプションが純粋に他の tween との互換性のために指定される場合、単に MAIN または INGRESS の fallback を追加してください。例えば、 under=('someothertween', 'someothertween2', INGRESS) 。この制約は、 tween を ‘someothertween’ tween, ‘someothertween2’ tween, INGRESS のすべての下に置くことを要求 します。これらのうちのいずれかが現在の設定にない場合は、この制約は単に 存在する tween に基づいて自分自身を組織するでしょう。

明示的な tween 順序

暗黙の tween 順序は明らかに単なるベストエフォートです。 Pyramid は、 add_tween() の呼び出しから得られる ヒントを用いてできるだけ暗黙の tween 順序を提供しようとしますが、 それは単なるベストエフォートなので、非常に正確な tween 順序が必要な場合、 それを得る唯一の方法は明示的な tween 順序を使用することです。 デプロイを行うユーザは、 pyramid.tweens 設定値を使用することで add_tween() の呼び出しによって暗黙 的に指定された tween の選択および順序を完全にオーバーライドすることができます。 この設定値が使われた場合、それは暗黙の tween チェイン中での tween ファクトリの順序 (または選択) をオーバーライドするような Python dotted names の リストでなければなりません。例えば:

1
2
3
4
5
6
7
8
9
[app:main]
use = egg:MyApp
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.tweens = myapp.my_cool_tween_factory
                 pyramid.tweens.excview_tween_factory

上記の設定では、設定中に行われた pyramid.config.Configurator.add_tween() の呼び出しは無視されます。 また、ユーザは pyramid.config.Configurator.add_tween() によって 追加された任意の tween ファクトリの代わりに pyramid.tweens 設定に リストした tween ファクトリ (各々は tween ファクトリを指す dotted Python name です) を使用するようにシステムに指示しています。 pyramid.tweens リスト中の 最初の tween ファクトリは、 有効な Pyramid リクエスト処理関数の producer として使用されるでしょう; それは直下で宣言された tween ファクトリをラップして、それが 無制限に続きます。「メイン」 Pyramid リクエストハンドラは暗黙的です。 そして常に「一番下」にあります。

Note

Pyramid 自身の exception view 処理ロジックは tween ファクトリ関数として実装されます: pyramid.tweens.excview_tween_factory() 。もし Pyramid 例外ビューの 処理が行われることを期待していて、 pyramid.tweens 設定で tween ファクトリを指定しているなら、 pyramid.tweens 設定リストに 明示的に pyramid.tweens.excview_tween_factory() 関数を追加 しなければなりません。もしそれが存在しなければ、 Pyramid は例外ビュー 処理を行ないません。

tween の衝突と順序の循環

Pyramid は、設定衝突検知を使用して同じ tween ファクトリが複数回 tween チェインに追加されることを防ぎます。設定に複数回同じ tween ファクトリを追加したければ、以下のことをする必要があります: a) それが 衝突するファクトリとは別の、グローバルにインポート可能なインスタンス オブジェクトである tween ファクトリを使用する b) tween ファクトリとして それが衝突する別の tween ファクトリと同じロジックで異なる __name__ 属性を持つ関数またはクラスを使用する、あるいは c) pyramid.config.Configurator.add_tween() の複数回の呼び出しの間に pyramid.config.Configurator.commit() を呼び出す。

“add_tween” への任意の呼び出しの中で overunder が使用されている 時に暗黙の tween 順序に循環が検知された場合、スタートアップ時に例外が発生 します。

tween 順序の表示

ptweens コマンドラインユーティリティを使って、アプリケーションによって 使用される現在の暗黙的および明示的な tween チェインを表示することができます。 Displaying “Tweens” を参照してください。

Adding A Third Party View, Route, or Subscriber Predicate

Note

Third-party view, route, and subscriber predicates are a feature new as of Pyramid 1.4.

View and Route Predicates

View and route predicates used during configuration allow you to narrow the set of circumstances under which a view or route will match. For example, the request_method view predicate can be used to ensure a view callable is only invoked when the request’s method is POST:

@view_config(request_method='POST')
def someview(request):
    ...

Likewise, a similar predicate can be used as a route predicate:

config.add_route('name', '/foo', request_method='POST')

Many other built-in predicates exists (request_param, and others). You can add third-party predicates to the list of available predicates by using one of pyramid.config.Configurator.add_view_predicate() or pyramid.config.Configurator.add_route_predicate(). The former adds a view predicate, the latter a route predicate.

When using one of those APIs, you pass a name and a factory to add a predicate during Pyramid’s configuration stage. For example:

config.add_view_predicate('content_type', ContentTypePredicate)

The above example adds a new predicate named content_type to the list of available predicates for views. This will allow the following view configuration statement to work:

1
2
@view_config(content_type='File')
def aview(request): ...

The first argument to pyramid.config.Configurator.add_view_predicate(), the name, is a string representing the name that is expected to be passed to view_config (or its imperative analogue add_view).

The second argument is a view or route predicate factory. A view or route predicate factory is most often a class with a constructor (__init__), a text method, a phash method and a __call__ method. For example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
 class ContentTypePredicate(object):
     def __init__(self, val, config):
         self.val = val

     def text(self):
         return 'content_type = %s' % (self.val,)

     phash = text

     def __call__(self, context, request):
         return getattr(context, 'content_type', None) == self.val

The constructor of a predicate factory takes two arguments: val and config. The val argument will be the argument passed to view_config (or add_view). In the example above, it will be the string File. The second arg, config will be the Configurator instance at the time of configuration.

The text method must return a string. It should be useful to describe the behavior of the predicate in error messages.

The phash method must return a string or a sequence of strings. It’s most often the same as text, as long as text uniquely describes the predicate’s name and the value passed to the constructor. If text is more general, or doesn’t describe things that way, phash should return a string with the name and the value serialized. The result of phash is not seen in output anywhere, it just informs the uniqueness constraints for view configuration.

The __call__ method of a predicate factory must accept a resource (context) and a request, and must return True or False. It is the “meat” of the predicate.

You can use the same predicate factory as both a view predicate and as a route predicate, but you’ll need to call add_view_predicate and add_route_predicate separately with the same factory.

Subscriber Predicates

Subscriber predicates work almost exactly like view and route predicates. They narrow the set of circumstances in which a subscriber will be called. There are several minor differences between a subscriber predicate and a view/route predicate:

  • There are no default subscriber predicates. You must register one to use one.
  • The __call__ method of a subscriber predicate accepts a single event object instead of a context and a request.
  • Not every subscriber predicate can be used with every event type. Some subscriber predicates will assume a certain event type.

Here’s an example of a subscriber predicate that can be used in conjunction with a subscriber that subscribes to the pyramid.events.NewReqest event type.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
 class RequestPathStartsWith(object):
     def __init__(self, val, config):
         self.val = val

     def text(self):
         return 'path_startswith = %s' % (self.val,)

     phash = text

     def __call__(self, event):
         return event.request.path.startswith(self.val)

Once you’ve created a subscriber predicate, it may registered via pyramid.config.Configurator.add_subscriber_predicate(). For example:

config.add_subscriber_predicate(
    'request_path_startswith', RequestPathStartsWith)

Once a subscriber predicate is registered, you can use it in a call to pyramid.config.Configurator.add_subscriber() or to pyramid.events.subscriber. Here’s an example of using the previously registered request_path_startswith predicate in a call to add_subscriber():

1
2
3
4
5
6
7
8
9
 # define a subscriber in your code

 def yosubscriber(event):
     event.request.yo = 'YO!'

 # and at configuration time

 config.add_subscriber(yosubscriber, NewRequest,
        request_path_startswith='/add_yo')

Here’s the same subscriber/predicate/event-type combination used via subscriber.

1
2
3
4
5
 from pyramid.events import subscriber

 @subscriber(NewRequest, request_path_startswith='/add_yo')
 def yosubscriber(event):
     event.request.yo = 'YO!'

In either of the above configurations, the yosubscriber callable will only be called if the request path starts with /add_yo. Otherwise the event subscriber will not be called.

Note that the request_path_startswith subscriber you defined can be used with events that have a request attribute, but not ones that do not. So, for example, the predicate can be used with subscribers registered for pyramid.events.NewRequest and pyramid.events.ContextFound events, but it cannot be used with subscribers registered for pyramid.events.ApplicationCreated because the latter type of event has no request attribute. The point being: unlike route and view predicates, not every type of subscriber predicate will necessarily be applicable for use in every subscriber registration. It is not the responsibility of the predicate author to make every predicate make sense for every event type; it is the responsibility of the predicate consumer to use predicates that make sense for a particular event type registration.