使用钩子

“钩子”可用于影响 Pyramid 以各种方式构建框架。

更改未找到视图

什么时候? Pyramid 无法将URL映射到查看代码,它将调用 Not Found View ,这是一个 view callable . 可以通过应用程序配置覆盖默认的未找到视图。

如果应用程序使用 imperative configuration ,可以使用 pyramid.config.Configurator.add_notfound_view() 方法:

1def notfound(request):
2    return Response('Not Found', status='404 Not Found')
3
4def main(globals, **settings):
5    config = Configurator()
6    config.add_notfound_view(notfound)

这个 Not Found View Callable和其他视图一样是一个可调用的视图。

如果您的应用程序使用 pyramid.view.view_config 装饰师和 scan ,可以使用 pyramid.view.notfound_view_config 装饰者:

1from pyramid.view import notfound_view_config
2
3@notfound_view_config()
4def notfound(request):
5    return Response('Not Found', status='404 Not Found')
6
7def main(globals, **settings):
8    config = Configurator()
9    config.scan()

这正是上面命令性示例所显示的。

您的应用程序可以定义 倍数 如有必要,找不到视图。两个 pyramid.config.Configurator.add_notfound_view()pyramid.view.notfound_view_config 采用与 pyramid.config.Configurator.add_viewpyramid.view.view_config ,分别。这意味着未找到的视图可以携带限制其适用性的谓词。例如:

 1from pyramid.view import notfound_view_config
 2
 3@notfound_view_config(request_method='GET')
 4def notfound_get(request):
 5    return Response('Not Found during GET', status='404 Not Found')
 6
 7@notfound_view_config(request_method='POST')
 8def notfound_post(request):
 9    return Response('Not Found during POST', status='404 Not Found')
10
11def main(globals, **settings):
12    config = Configurator()
13    config.scan()

这个 notfound_get 当找不到视图且请求方法为 GET . 这个 notfound_post 当找不到视图且请求方法为 POST .

与任何其他视图一样,未找到的视图必须至少接受 request 参数,或两者 contextrequest . 这个 request 就是电流 request 表示被拒绝的操作。这个 context (如果在呼叫签名中使用)将是 HTTPNotFound 导致调用视图的异常。

两个 pyramid.config.Configurator.add_notfound_view()pyramid.view.notfound_view_config 可用于自动将请求重定向到斜线追加的路由。见 重定向到斜线附加路由 举个例子。

下面是一些实现最小值的示例代码 Not Found View 可调用的:

1from pyramid.httpexceptions import HTTPNotFound
2
3def notfound(request):
4    return HTTPNotFound()

备注

当调用未找到的视图可调用时,它将 request . 这个 exception 请求的属性将是 HTTPNotFound 导致调用未找到视图的异常。价值 request.exception.message 将是一个值,用于解释引发未找到异常的原因。此消息具有不同的值,具体取决于 pyramid.debug_notfound 环境设置为真或假。

备注

当未找到的视图可调用接受参数列表时,如中所述 可选视图可调用参数/调用约定 , the context 作为第一个参数传递给视图callable将是 HTTPNotFound 异常实例。如果可用,资源上下文将仍然可用 request.context .

警告

这个 Not Found View 仅当 HTTPNotFound 引发异常。如果从视图返回异常,那么它将被视为常规响应对象,并且不会触发自定义视图。

更改禁止的视图

什么时候? Pyramid 无法根据授权执行视图 authorization policy 在使用中,它调用 forbidden view . 默认禁止响应具有403状态代码,并且非常简单,但是生成它的视图可以根据需要重写。

这个 forbidden view Callable和其他视图一样是一个可调用的视图。这个 view configuration 这使得它成为一个“禁止”的视图,包括使用 pyramid.config.Configurator.add_forbidden_view() API或 pyramid.view.forbidden_view_config 装饰者。

例如,可以使用 pyramid.config.Configurator.add_forbidden_view() 注册禁止的视图的方法:

1def forbidden(request):
2    return Response('forbidden')
3
4def main(globals, **settings):
5    config = Configurator()
6    config.add_forbidden_view(forbidden)

如果你喜欢用装饰和 scan ,您可以使用 pyramid.view.forbidden_view_config decorator将可调用视图标记为禁止视图:

1from pyramid.view import forbidden_view_config
2
3@forbidden_view_config()
4def forbidden(request):
5    return Response('forbidden')
6
7def main(globals, **settings):
8    config = Configurator()
9    config.scan()

与任何其他视图一样,禁止视图必须至少接受 request 参数,或两者 contextrequest . 如果禁止的视图Callable同时接受 contextrequest ,HTTP异常作为上下文传递。这个 context 正如路由器在拒绝视图时发现的(通常您会期望)可用的 request.context . 这个 request 就是电流 request 表示被拒绝的操作。

下面是一些实现最小禁止视图的示例代码:

1from pyramid.view import view_config
2from pyramid.response import Response
3
4def forbidden_view(request):
5    return Response('forbidden')

备注

当调用禁止的视图可调用时,它将 request . 这个 exception 请求的属性将是 HTTPForbidden 导致调用禁止的视图的异常。价值 request.exception.message 将是解释为什么引发禁止异常的一个值,以及 request.exception.result 将扩展有关禁止例外的信息。这些消息具有不同的值,具体取决于 pyramid.debug_authorization 环境设置为真或假。

警告

这个 forbidden view 仅当 HTTPForbidden 引发异常。如果从视图返回异常,那么它将被视为常规响应对象,并且不会触发自定义视图。

更改请求工厂

无论何时 Pyramid 处理来自的请求 WSGI 服务器,它创建一个 request 基于wsgi环境的对象已被传递。默认情况下, pyramid.request.Request 创建类来表示请求对象。

等级(A.K.A.,“工厂”)为 Pyramid 创建请求对象实例的用途可以通过传递 request_factory 的构造函数的参数 configurator . 此参数可以是可调用的或 dotted Python name 表示可调用的。

1from pyramid.request import Request
2
3class MyRequest(Request):
4    pass
5
6config = Configurator(request_factory=MyRequest)

如果您正在进行命令式配置,并且您宁愿在已经构建了 configurator ,也可以通过 pyramid.config.Configurator.set_request_factory() 方法:

1from pyramid.config import Configurator
2from pyramid.request import Request
3
4class MyRequest(Request):
5    pass
6
7config = Configurator()
8config.set_request_factory(MyRequest)

向请求对象添加方法或属性

在 1.4 版本加入.

因为每个金字塔应用程序只能有一个 request 工厂, changing the request factory 不是那么可扩展,特别是如果您想要构建可组合的特性(例如金字塔插件和插件)。

Lazy属性可以通过 pyramid.config.Configurator.add_request_method() 应用程序编程接口。这允许您指定在请求对象上可用的可调用文件,但在访问之前不会实际执行函数。

警告

这将以静默方式重写中的方法和属性 request factory 同名的。

 1from pyramid.config import Configurator
 2
 3def total(request, *args):
 4    return sum(args)
 5
 6def prop(request):
 7    print("getting the property")
 8    return "the property"
 9
10config = Configurator()
11config.add_request_method(total)
12config.add_request_method(prop, reify=True)

在上面的例子中, total 作为方法添加。然而, prop 作为属性添加,并通过设置缓存每个请求的结果 reify=True . 这样,我们就消除了多次运行函数的开销。

>>> request.total(1, 2, 3)
6
>>> request.prop
getting the property
'the property'
>>> request.prop
'the property'

不缓存的结果 request.prop ,集合 property=True 而不是 reify=True .

下面是将类传递给 Configurator.add_request_method

 1from pyramid.config import Configurator
 2from pyramid.decorator import reify
 3
 4class ExtraStuff(object):
 5
 6    def __init__(self, request):
 7        self.request = request
 8
 9    def total(self, *args):
10        return sum(args)
11
12    # use @property if you don't want to cache the result
13    @reify
14    def prop(self):
15        print("getting the property")
16        return "the property"
17
18config = Configurator()
19config.add_request_method(ExtraStuff, 'extra', reify=True)

我们附加并缓存一个名为 extrarequest 对象。

>>> request.extra.total(1, 2, 3)
6
>>> request.extra.prop
getting the property
'the property'
>>> request.extra.prop
'the property'

更改响应工厂

在 1.6 版本加入.

无论何时 Pyramid 从视图返回响应,它将创建 response 对象。默认情况下, pyramid.response.Response 创建类来表示响应对象。

那家工厂 Pyramid 用于创建响应对象实例的方法可以通过传递 pyramid.interfaces.IResponseFactory 的构造函数的参数 configurator . 此参数可以是可调用的或 dotted Python name 表示可调用的。

工厂接受单个位置参数,这是 Request 对象。参数可能是 None .

1from pyramid.response import Response
2
3class MyResponse(Response):
4    pass
5
6config = Configurator(response_factory=lambda r: MyResponse())

如果您正在进行命令式配置,并且希望在构建 configurator ,也可以通过 pyramid.config.Configurator.set_response_factory() 方法:

1from pyramid.config import Configurator
2from pyramid.response import Response
3
4class MyResponse(Response):
5    pass
6
7config = Configurator()
8config.set_response_factory(lambda r: MyResponse())

使用“渲染前”事件

订阅 pyramid.events.BeforeRender 事件可能会反省和修改 renderer globals 在他们被传给 renderer . 此事件对象iself具有类似字典的接口,可用于此目的。例如:

1from pyramid.events import subscriber
2from pyramid.events import BeforeRender
3
4@subscriber(BeforeRender)
5def add_global(event):
6    event['mykey'] = 'foo'

此类型的对象将作为事件发送到 renderer 被调用。

如果订阅服务器尝试添加已存在于渲染器全局字典中的键,则 KeyError 提高了。这一限制是强制执行的,因为事件订阅服务器不具有任何相对顺序。全部添加到渲染器全局字典的键集 pyramid.events.BeforeRender 订阅服务器和渲染器全局工厂必须是唯一的。

从视图返回的词典可以通过 rendering_val 的属性 BeforeRender 事件。

假设你回来了 {{'mykey': 'somevalue', 'mykey2': 'somevalue2'}} 从您的角度来看,callable是这样的:

1from pyramid.view import view_config
2
3@view_config(renderer='some_renderer')
4def myview(request):
5    return {'mykey': 'somevalue', 'mykey2': 'somevalue2'}

rendering_val 可用于从 BeforeRender 对象:

1from pyramid.events import subscriber
2from pyramid.events import BeforeRender
3
4@subscriber(BeforeRender)
5def read_return(event):
6    # {'mykey': 'somevalue'} is returned from the view
7    print(event.rendering_val['mykey'])

有关 BeforeRender 事件接口位于 pyramid.interfaces.IBeforeRender .

使用响应回调

与许多其他Web框架不同, Pyramid 不急于创建全局响应对象。添加一个 response callback 允许应用程序注册要对视图返回的任何响应对象执行的操作,通常是为了改变响应。

这个 pyramid.request.Request.add_response_callback() 方法用于注册响应回调。

响应回调是可调用的,它接受两个位置参数: requestresponse . 例如:

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

如果应用程序代码中发生未处理的异常,或者如果由 view callable 无效。响应回调 are 但是,当 exception view 已成功呈现。在这种情况下, request.exception 请求进入响应回调时的属性将是异常对象,而不是其默认值 None .

响应回调按添加的顺序调用(首先调用最近添加的)。调用所有响应回调 之前 这个 NewResponse 事件已发送。响应回调引发的错误不会被特殊处理。它们将传播到 Pyramid 路由器应用程序。

响应回调的生存期为 单一的 请求。如果希望响应回调发生在 每一个 请求时,必须将回调重新注册到每个新请求中(可能在 NewRequest 事件)。

使用完成的回调

A finished callback 是将由 Pyramid router 在请求处理的最后。完成的回调可用于无条件地在请求结束时执行操作。

这个 pyramid.request.Request.add_finished_callback() 方法用于注册完成的回调。

完成的回调是可调用的,它接受单个位置参数: request . 例如:

1import logging
2
3log = logging.getLogger(__name__)
4
5def log_callback(request):
6    """Log information at the end of request"""
7    log.debug('Request is finished.')
8request.add_finished_callback(log_callback)

完成的回调按添加顺序调用(首先调用最近添加的回调)。已完成回调(与 response callback总是 调用,即使应用程序代码中发生了阻止生成响应的异常。

调用与请求关联的已完成回调集 很晚 在处理该请求时;它们本质上是 router 在请求“结束”之前。在顶级响应处理完成后调用它们 finally: 在路由器请求处理代码中阻塞。结果,突变发生在 request 提供给完成的回调将没有任何意义的效果,因为响应处理将已经发生,并且在处理完所有完成的回调之后,请求的作用域将几乎立即过期。

完成的回调引发的错误不会被特殊处理。它们将传播到 Pyramid 路由器应用程序。

完成的回调的生存期为 单一的 请求。如果您希望在 每一个 请求时,必须将回调重新注册到每个新请求中(可能在 NewRequest 事件)。

更改遍历器

默认值 traversal 算法 Pyramid 使用说明见 遍历算法 . 尽管很少有必要,但可以通过配置有选择地将此默认算法换成不同的遍历模式。

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

在上面的例子中, myapp.traversal.Traverser 假定为实现以下接口的类:

 1class Traverser(object):
 2    def __init__(self, root):
 3        """ Accept the root object returned from the root factory """
 4
 5    def __call__(self, request):
 6        """ Return a dictionary with (at least) the keys ``root``,
 7        ``context``, ``view_name``, ``subpath``, ``traversed``,
 8        ``virtual_root``, and ``virtual_root_path``.  These values are
 9        typically the result of a resource tree traversal.  ``root``
10        is the physical root object, ``context`` will be a resource
11        object, ``view_name`` will be the view name used (a string),
12        ``subpath`` will be a sequence of strings that
13        followed the view name but were not traversed, ``traversed``
14        will be a sequence of strings that were traversed
15        (including the virtual root path, if any) ``virtual_root``
16        will be a resource object representing the virtual root (or the
17        physical root if traversal was not performed), and
18        ``virtual_root_path`` will be a sequence representing the
19        virtual root path (a sequence of strings) or ``None`` if
20        traversal was not performed.
21
22        Extra keys for special purpose functionality can be added as
23        necessary.
24
25        All values returned in the dictionary will be made available
26        as attributes of the ``request`` object.
27        """

可以同时激活多个遍历算法。例如,如果 root factory 有条件地返回多个对象类型,您可以声明替代遍历器适配器仅“for”一个特定的类或接口。当根工厂返回实现该类或接口的对象时,将使用自定义遍历器。否则将使用默认的遍历器。例如:

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

如果上面的诗节被添加到金字塔中 __init__.py 文件的 main 函数, Pyramid 将使用 myapp.traversal.Traverser 只有当应用程序 root factory 返回的实例 myapp.resources.MyRoot 对象。否则它将使用默认值 Pyramid 进行遍历。

如何改变 pyramid.request.Request.resource_url() 生成URL

如中所述添加遍历器时 更改遍历器 ,继续使用 pyramid.request.Request.resource_url() 应用程序编程接口。但是,由于遍历的完成方式将被修改,因此当用于从自定义遍历器派生的资源时,它默认生成的URL可能不正确。

如果添加了遍历器,则可以更改 resource_url() 通过添加对的调用为特定类型的资源生成URL pyramid.config.Configurator.add_resource_url_adapter() .

例如:

1from myapp.traversal import ResourceURLAdapter
2from myapp.resources import MyRoot
3
4config.add_resource_url_adapter(ResourceURLAdapter, MyRoot)

在上面的示例中, myapp.traversal.ResourceURLAdapter 类将用于向 resource_url() 任何时候 resource 传递给 resource_url 是班里的 myapp.resources.MyRoot . 这个 resource_iface 论点 MyRoot 表示要找到此资源URL工厂,资源必须拥有的接口类型。如果 resource_iface 参数被省略,此资源URL适配器将用于 all 资源。

必须由提供 IResourceURL 如下:

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

默认的上下文URL生成器作为类可供阅读。 pyramid.traversal.ResourceURLtraversal module .

pyramid.config.Configurator.add_resource_url_adapter() 更多信息。

更改金字塔处理视图响应的方式

在 1.1 版本加入.

通过使用包含 pyramid.config.Configurator.add_response_adapter()response_adapter 装饰者。

金字塔,在不同的地方,适应调用可调用视图的结果 IResponse 接口以确保视图可调用返回的对象是“true”响应对象。绝大多数情况下,这种改编的结果是结果对象本身,因为阅读本手册中的叙述性文档的“平民”编写的视图可调用文件将始终返回实现 IResponse 接口。通常,这是 pyramid.response.Response 类或子类。如果平民从未配置为使用 renderer 他们通常会期望路由器出现错误。但是,您可以钩住金字塔,这样用户就可以通过提供适配器从可调用视图返回任意值,该适配器将任意返回值转换为实现 IResponse .

例如,如果希望允许View Callables返回裸字符串对象(不需要 renderer 要将字符串转换为响应对象,可以注册适配器,该适配器将字符串转换为响应:

1from pyramid.response import Response
2
3def string_response_adapter(s):
4    response = Response(s)
5    return response
6
7# config is an instance of pyramid.config.Configurator
8
9config.add_response_adapter(string_response_adapter, str)

同样,如果您希望能够从View Callables返回简化类型的响应对象,那么可以使用IResponse钩子将适配器注册到更复杂的IResponse接口:

 1from pyramid.response import Response
 2
 3class SimpleResponse(object):
 4    def __init__(self, body):
 5        self.body = body
 6
 7def simple_response_adapter(simple_response):
 8    response = Response(simple_response.body)
 9    return response
10
11# config is an instance of pyramid.config.Configurator
12
13config.add_response_adapter(simple_response_adapter, SimpleResponse)

如果要实现自己的响应对象而不是使用 pyramid.response.Response 对象在任何容量下,都必须确保该对象实现中概述的每个属性和方法。 pyramid.interfaces.IResponse 你必须确保它 zope.interface.implementer(IResponse) 作为一个班级装饰师。

1from pyramid.interfaces import IResponse
2from zope.interface import implementer
3
4@implementer(IResponse)
5class MyResponse(object):
6    # ... an implementation of every method and attribute
7    # documented in IResponse should follow ...

当可调用视图返回备用响应对象实现时,如果该对象断言它实现 IResponse (通过 zope.interface.implementer(IResponse) )不需要为对象注册适配器;金字塔将直接使用它。

IResponse适配器,用于 webob.reponse.Response (相对于 pyramid.response.Response )默认情况下由金字塔在启动时注册,根据其性质,此类的实例(以及类的子类的实例)将本机提供IResponse。注册的适配器 webob.reponse.Response 简单地返回响应对象。

而不是使用 pyramid.config.Configurator.add_response_adapter() ,您可以使用 pyramid.response.response_adapter 装饰者:

1from pyramid.response import Response
2from pyramid.response import response_adapter
3
4@response_adapter(str)
5def string_response_adapter(s):
6    response = Response(s)
7    return response

上面的示例在扫描时具有与以下相同的效果:

config.add_response_adapter(string_response_adapter, str)

这个 response_adapter 直到被激活 scan .

使用视图映射器

视图可调用文件的默认调用约定记录在 意见 章。您可以通过使用 view mapper .

视图映射器是一个接受一组关键字参数并返回可调用的对象。返回的可调用文件是用 view callable 对象。返回的可调用项本身应返回另一个可调用项,可使用“内部调用协议”调用该可调用项。 (context, request) .

可以通过多种方式使用视图映射器:

  • 通过设置 __view_mapper__ 视图可调用本身的属性(即视图映射器对象)

  • 通过将映射器对象传递到 pyramid.config.Configurator.add_view() (或其声明性和装饰性等价物)作为 mapper 论点

  • 通过注册 违约 视图映射器

这里有一个视图映射器的例子,它模拟(某种程度上)一个塔架“控制器”。映射器是用一些关键字参数初始化的。它的 __call__ 方法接受视图对象(将是类)。它使用 attr 关键字参数,它被传递来确定应将哪个属性用作操作方法。它返回的包装方法接受 (context, request) 并返回使用关键字参数调用action方法的结果 matchdict 在弹出 action 离开它。这在一定程度上模拟了桥塔式的调用操作方法,将路由参数从路由中拉出,并将dict匹配为关键字参数。

 1# framework
 2
 3class PylonsControllerViewMapper(object):
 4    def __init__(self, **kw):
 5        self.kw = kw
 6
 7    def __call__(self, view):
 8        attr = self.kw['attr']
 9        def wrapper(context, request):
10            matchdict = request.matchdict.copy()
11            matchdict.pop('action', None)
12            inst = view(request)
13            meth = getattr(inst, attr)
14            return meth(**matchdict)
15        return wrapper
16
17class BaseController(object):
18    __view_mapper__ = PylonsControllerViewMapper

用户可以这样使用这些框架组件:

 1# user application
 2
 3from pyramid.response import Response
 4from pyramid.config import Configurator
 5import pyramid_handlers
 6from wsgiref.simple_server import make_server
 7
 8class MyController(BaseController):
 9    def index(self, id):
10        return Response(id)
11
12if __name__ == '__main__':
13    config = Configurator()
14    config.include(pyramid_handlers)
15    config.add_handler('one', '/{id}', MyController, action='index')
16    config.add_handler('two', '/{action}/{id}', MyController)
17    server.make_server('0.0.0.0', 8080, config.make_wsgi_app())
18    server.serve_forever()

这个 pyramid.config.Configurator.set_view_mapper() 方法可用于设置 违约 视图映射器(覆盖金字塔本身使用的超默认视图映射器)。

A 单一的 视图注册可以通过将映射器作为 mapper 参数 add_view() .

正在注册配置修饰符

装饰师,如 view_config 不要改变正在修饰的函数或类的行为。相反,当 scan 执行时,函数或类的修改版本将注册到 Pyramid .

你可能希望有自己的装饰师提供这种行为。这可以通过使用 Venusian 包装方式与 Pyramid .

举例来说,假设您想要编写一个修饰器,用一个 Zope Component Architecture 中的“实用程序” application registry 提供的 Pyramid . 只有在应用程序的配置至少部分完成之后,注册表中的应用程序注册表和实用程序才可能可用。一个普通的装饰器将失败,因为它将在配置开始之前执行。

然而,使用 Venusian ,可以这样写装饰器:

 1import venusian
 2from mypackage.interfaces import IMyUtility
 3
 4class registerFunction(object):
 5
 6    def __init__(self, path):
 7        self.path = path
 8
 9    def register(self, scanner, name, wrapped):
10        registry = scanner.config.registry
11        registry.getUtility(IMyUtility).register(
12            self.path, wrapped)
13
14    def __call__(self, wrapped):
15        venusian.attach(wrapped, self.register)
16        return wrapped

然后可以使用这个修饰器在整个代码中注册函数:

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

但是,只有当 scan 已执行,使您能够提前设置实用程序:

 1from zope.interface import implementer
 2
 3from wsgiref.simple_server import make_server
 4from pyramid.config import Configurator
 5from mypackage.interfaces import IMyUtility
 6
 7@implementer(IMyUtility)
 8class UtilityImplementation:
 9
10    def __init__(self):
11        self.registrations = {}
12
13    def register(self, path, callable_):
14        self.registrations[path] = callable_
15
16if __name__ == '__main__':
17    config = Configurator()
18    config.registry.registerUtility(UtilityImplementation())
19    config.scan()
20    app = config.make_wsgi_app()
21    server = make_server('0.0.0.0', 8080, app)
22    server.serve_forever()

有关详细信息,请阅读 Venusian documentation .

登记吐温

在 1.2 版本加入: 吐温

A tween (单词“between”的缩写)是位于金字塔路由器的主要请求处理功能和上游使用的wsgi组件之间的代码。 Pyramid 作为它的“应用程序”。这是一个特性,金字塔框架扩展可以使用它来提供,例如,金字塔特定的视图定时支持簿记代码,在将异常返回到上游的wsgi应用程序之前检查异常。吐温的表现有点像 WSGI middleware 但是他们有在一个他们可以进入金字塔的环境中运行的好处。 requestresponseapplication registry 以及金字塔渲染机器。

创造一个中间人

要创建Tween,必须编写“Tween工厂”。tween工厂必须是全局可导入可调用的,它接受两个参数: handlerregistry . handler 将是主金字塔请求处理功能或另一个tween。 registry 将是金字塔 application registry 由这个配置器表示。Tween工厂在调用Tween(可调用对象)时必须将其返回。

用一个单独的参数来调用tween, request ,这就是 request 当金字塔的路由器接收到wsgi请求时创建。吐温应该返回 response 通常是由下游金字塔应用程序生成的。

您可以将Tween工厂编写为一个简单的关闭返回函数:

 1def simple_tween_factory(handler, registry):
 2    # one-time configuration code goes here
 3
 4    def simple_tween(request):
 5        # code to be executed for each request before
 6        # the actual application code goes here
 7
 8        response = handler(request)
 9
10        # code to be executed for each request after
11        # the actual application code goes here
12
13        return response
14
15    return simple_tween

或者,Tween工厂可以是 __call__ 魔术法:

 1class simple_tween_factory(object):
 2    def __init__(self, handler, registry):
 3        self.handler = handler
 4        self.registry = registry
 5
 6        # one-time configuration code goes here
 7
 8    def __call__(self, request):
 9        # code to be executed for each request before
10        # the actual application code goes here
11
12        response = self.handler(request)
13
14        # code to be executed for each request after
15        # the actual application code goes here
16
17        return response

您应该避免在tween实例上改变任何状态。每个请求调用一次tween,需要小心处理任何共享可变状态,以避免出现任何竞争条件。

闭包样式的性能稍好一些,使您能够有条件地从请求处理管道中省略tween(参见下面的timing tween示例),而类样式使共享可变状态和允许子类化变得更容易。

下面是一个完整的tween示例,记录处理每个请求所花费的时间:

 1# in a module named myapp.tweens
 2
 3import time
 4from pyramid.settings import asbool
 5import logging
 6
 7log = logging.getLogger(__name__)
 8
 9def timing_tween_factory(handler, registry):
10    if asbool(registry.settings.get('do_timing')):
11        # if timing support is enabled, return a wrapper
12        def timing_tween(request):
13            start = time.time()
14            try:
15                response = handler(request)
16            finally:
17                end = time.time()
18                log.debug('The request took %s seconds' %
19                          (end - start))
20            return response
21        return timing_tween
22    # if timing support is not enabled, return the original
23    # handler
24    return handler

在上面的示例中,Tween工厂定义了 timing_tween 吐温,如果 asbool(registry.settings.get('do_timing')) 是真的。否则,它只返回给它的处理程序。这个 registry.settings 属性是用户提供的部署设置的句柄(通常在 .ini 文件)。在这种情况下,如果用户定义了 do_timing 设置,该设置为 True ,用户表示要做定时,所以tween工厂返回定时tween;否则只返回它提供的处理程序,防止任何定时。

示例时间间隔只记录开始时间,调用下游处理程序,记录下游处理程序消耗的秒数,并返回响应。

注册隐式Tween工厂

创建tween工厂后,可以使用 pyramid.config.Configurator.add_tween() 使用ITS的方法 dotted Python name .

下面是一个在金字塔应用程序中将Tween工厂注册为“隐式”Tween的示例:

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

请注意,您必须使用 dotted Python name 作为第一个论点 pyramid.config.Configurator.add_tween() 这必须指向一家吐温工厂。不能将tween工厂对象本身传递给方法:它必须 dotted Python name 指向一个全局可导入的对象。在上面的例子中,我们假设 timing_tween_factory Tween工厂是在一个名为 myapp.tweens 所以吐温工厂是进口的 myapp.tweens.timing_tween_factory .

当你使用 pyramid.config.Configurator.add_tween() ,您指示系统在启动时使用Tween工厂,除非用户在配置中提供了明确的Tween列表。这就是所谓的“隐性”中间人。用户总是可以选择提供一个显式的tween列表,重新排序或取消隐式添加tween。见 显式吐温排序 有关显式tween排序的更多信息。

如果有多个呼叫 pyramid.config.Configurator.add_tween() 在单个应用程序配置中进行,在应用程序启动时,吐温将被链接在一起。这个 第一 吐温工厂通过 add_tween 将以工厂之间的金字塔例外视图作为其调用 handler 参数,然后直接在后面添加的tween工厂将调用第一个tween工厂的结果作为它的 handler 参数,等等,直到所有的工厂都被调用。金字塔路由器将使用这个链产生的最外层tween(由最后一个tween工厂添加的tween产生)作为其请求处理函数。例如:

1from pyramid.config import Configurator
2
3config = Configurator()
4config.add_tween('myapp.tween_factory1')
5config.add_tween('myapp.tween_factory2')

上面的例子将生成一个类似这样的隐式tween链。

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

暗示隐式吐温排序

默认情况下,如上所述,链的顺序完全由调用的相对顺序控制 pyramid.config.Configurator.add_tween() . 但是,呼叫方 add_tween 可以提供一个可选提示,通过提供 underover (或两者)的论点 add_tween() . 这些提示仅在不使用显式tween顺序时使用。见 显式吐温排序 有关如何设置显式tween排序的说明。

允许值 underover (或两者兼而有之):

  • None (违约),

  • dotted Python name 到tween工厂:一个字符串,表示在调用中添加的tween工厂的预测点名称。 add_tween 在同一个配置会话中,

  • 常数之一 pyramid.tweens.MAINpyramid.tweens.INGRESSpyramid.tweens.EXCVIEW

  • 上述任何组合都不可重复。这允许用户指定回退(如果不包括所需的tween)以及与多个其他tween的兼容性。

有效地, over 表示“接近请求入口,而不是” under 意思是“比更接近主金字塔应用程序”。你可以想象一个洋葱,外层在内层,应用程序在中间的所有层下面。

例如,以下调用 add_tween() 将尝试将吐温工厂 myapp.tween_factory 直接“在上面”(in ptweens order)主金字塔请求处理程序。

1import pyramid.tweens
2
3config.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工厂之下:

1import pyramid.tweens
2
3config.add_tween('myapp.tween_factory1',
4                 over=pyramid.tweens.MAIN)
5config.add_tween('myapp.tween_factory2',
6                 over=pyramid.tweens.MAIN,
7                 under='myapp.tween_factory1')

上面的示例将生成一个隐式的tween链,如下所示:

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

两者都不指定 over 也不 under 相当于指定 under=INGRESS .

如果所有选项 under (或) over )在当前配置中找不到,这是一个错误。如果某些选项纯粹是为了与其他tween兼容而指定的,只需添加 MAININGRESS . 例如, under=('someothertween', 'someothertween2', INGRESS) . 这一限制将要求二者之间位于 someothertween 吐温 someothertween2 吐温 INGRESS . 如果其中任何一个不在当前配置中,那么这个约束将只根据存在的中间层组织自己。

显式吐温排序

显然,隐式二元排序只是最好的工作。金字塔将尽可能利用调用提供的提示提供一个隐含的花间顺序。 add_tween() . 但是,因为这只是最好的努力,如果需要非常精确的tween顺序,唯一可靠的方法是使用一个明确的tween顺序。部署用户可以重写调用所隐含的包含和排序之间的隐式 add_tween() 完全通过使用 pyramid.tweens 设置值。使用时,此设置值必须是一个以python为圆点的名称列表,该列表将覆盖隐式tween链中tween工厂的排序(和包含)。例如:

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

在上述配置中,在配置期间调用 pyramid.config.Configurator.add_tween() 被忽略了,用户告诉系统使用他列在 pyramid.tweens 配置设置(每个都是 dotted Python name 指的是吐温工厂),而不是通过 pyramid.config.Configurator.add_tween() . 这个 第一 吐温工厂 pyramid.tweens 列表将用作有效 Pyramid 请求处理功能;它将把直接声明为“低于”它的吐温工厂包装起来,无限大。“主”金字塔请求处理程序是隐式的,并且总是“在底部”。

备注

金字塔自身 exception view 处理逻辑作为tween工厂功能实现: pyramid.tweens.excview_tween_factory() . 如果需要金字塔异常视图处理,并且通过 pyramid.tweens 配置设置, pyramid.tweens.excview_tween_factory() 函数必须添加到 pyramid.tweens 配置设置列表。如果它不存在,金字塔将不执行异常视图处理。

冲突与排序周期之间

金字塔将防止相同的吐温工厂被添加到吐温链多次使用配置冲突检测。如果您希望在配置中多次添加同一个tween工厂,您应该:(a)使用一个tween工厂,它是一个独立的全局可导入实例对象,它与工厂冲突;(b)使用一个函数或类作为tween工厂,其逻辑与它冲突的另一个tween工厂相同,但使用不同的 __name__ 属性;或(c)调用 pyramid.config.Configurator.commit() 在呼叫到之间 pyramid.config.Configurator.add_tween() .

如果检测到一个周期在隐式的中间排序时 overunder 用于任何呼叫 add_tween ,启动时将引发异常。

显示吐温排序

这个 ptweens 命令行实用程序可用于报告应用程序当前使用的隐式和显式tween链。看到了吗 ptweens :显示“Tweens” .

添加自定义视图、路由或订阅服务器谓词

在 1.4 版本加入.

查看和路由谓词

配置期间使用的视图和路由谓词允许您缩小视图或路由匹配的环境集。例如, request_method 视图谓词可用于确保仅当请求的方法为 POST

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

同样,类似的谓词也可以用作 路线 谓语:

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

存在许多其他内置谓词 (request_param ,以及其他)。可以使用以下方法之一将自定义谓词添加到可用谓词列表中 pyramid.config.Configurator.add_view_predicate()pyramid.config.Configurator.add_route_predicate() . 前者添加视图谓词,后者添加路由谓词。

当使用这些API之一时,您将传递 name 和A 工厂 在金字塔的配置阶段添加谓词。例如:

config.add_view_predicate('content_type', ContentTypePredicate)

上面的示例添加了一个名为 content_type 到视图的可用谓词列表。这将允许以下视图配置语句工作:

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

第一个论点 pyramid.config.Configurator.add_view_predicate() 名称是一个字符串,表示预期要传递到的名称 view_config (或其强制性模拟) add_view

第二个参数是视图或路由谓词工厂,或者 dotted Python name 指的是视图或路由谓词工厂。视图或路由谓词工厂通常是带有构造函数的类 (__init__ a) text 方法,A phash 方法和A __call__ 方法。例如:

 1class ContentTypePredicate(object):
 2    def __init__(self, val, info):
 3        self.val = val
 4
 5    def text(self):
 6        return 'content_type = %s' % (self.val,)
 7
 8    phash = text
 9
10    def __call__(self, context, request):
11        return request.content_type == self.val

一个 pyramid.interfaces.IPredicateFactory 接受两个参数: ``val`info . 这个 val 参数将是传递给 view_config (或) add_view ). 在上面的示例中,它将是字符串 File . 第二个论点, info ,将是 pyramid.interfaces.IPredicateInfo 相对于配置谓词的操作创建的实例。这意味着 info.package value是调用操作的包,传入 valinfo.maybe_dotted 也是相对于这个包。

这个 text 方法必须返回字符串。在错误消息中描述谓词的行为应该很有用。

这个 phash 方法必须返回字符串或字符串序列。它通常与 text 只要 text 唯一描述谓词的名称和传递给构造函数的值。如果 text 更一般,或者不这样描述, phash 应返回名为且值已序列化的字符串。结果 phash 在输出的任何位置都看不到,它只是通知视图配置的唯一性约束。

这个 __call__ 方法根据谓词是否用作 view predicate 或A route predicate

  • 当用作路由谓词时, __call__ 签名是 (info, request) . 这个 info 对象是包含两个键的字典: matchroute . info['match'] 包含路由模式中匹配的模式的matchdict。 info['route']pyramid.interfaces.IRoute 当前路由的对象。

  • 当用作视图谓词时, __call__ 签名是 (context, request) . 这个 context 是的结果 traversal 使用任一路由执行 root factory 或者应用程序的 default root factory .

在所有情况下 __call__ 方法应返回 TrueFalse .

可以使用与视图谓词和路由谓词相同的谓词工厂,但它们需要处理 infocontext 专门的参数(许多谓词不需要这个参数),您需要调用 add_view_predicateadd_route_predicate 与同一工厂分开。

订阅服务器谓词

订阅服务器谓词的工作方式几乎与视图和路由谓词完全相同。它们缩小了用户被呼叫的情况范围。订阅服务器谓词和视图或路由谓词之间有几个细微的区别:

  • 没有默认的订阅服务器谓词。您必须注册一个才能使用。

  • 这个 __call__ 订阅服务器谓词的方法接受单个 event 对象而不是 context 和A request .

  • 并非每个订阅服务器谓词都可以用于每个事件类型。某些订阅服务器谓词将采用特定的事件类型。

下面是一个订阅服务器谓词的示例,它可以与订阅 pyramid.events.NewRequest 事件类型。

 1class RequestPathStartsWith(object):
 2    def __init__(self, val, info):
 3        self.val = val
 4
 5    def text(self):
 6        return 'request_path_startswith = %s' % (self.val,)
 7
 8    phash = text
 9
10    def __call__(self, event):
11        return event.request.path.startswith(self.val)

创建订阅服务器谓词后,可以通过 pyramid.config.Configurator.add_subscriber_predicate() . 例如:

config.add_subscriber_predicate(
    'request_path_startswith', RequestPathStartsWith)

一旦注册了订阅服务器谓词,就可以在调用 pyramid.config.Configurator.add_subscriber()pyramid.events.subscriber . 下面是使用以前注册的 request_path_startswith 调用中的谓词 add_subscriber()

1# define a subscriber in your code
2
3def yosubscriber(event):
4    event.request.yo = 'YO!'
5
6# and at configuration time
7
8config.add_subscriber(yosubscriber, NewRequest,
9                      request_path_startswith='/add_yo')

以下是通过以下方式使用的相同订阅服务器/谓词/事件类型组合: subscriber .

1from pyramid.events import subscriber
2
3@subscriber(NewRequest, request_path_startswith='/add_yo')
4def yosubscriber(event):
5    event.request.yo = 'YO!'

在上述任一配置中, yosubscriber 仅当请求路径以开头时才调用Callable /add_yo . 否则将不调用事件订阅服务器。

请注意 request_path_startswith 您定义的订阅服务器可以用于具有 request 属性,但不是不属性。因此,例如,谓词可以用于注册的订阅服务器 pyramid.events.NewRequestpyramid.events.ContextFound 事件,但不能用于注册的订阅服务器 pyramid.events.ApplicationCreated 因为后一种类型的事件没有 request 属性。这一点与路由和视图谓词不同,并不是所有类型的订户谓词都必须适用于每个订户注册。使每个谓词对每个事件类型都有意义不是谓词作者的责任;使用对特定事件类型注册有意义的谓词是谓词使用者的责任。

视图推导程序

在 1.7 版本加入.

由处理的每个URL Pyramid 与自定义视图管道匹配。见 请求处理 这是如何运作的。视图管道本身是根据用户提供的 view callable ,然后由 view derivers . 视图派生器是视图管道的可组合元素,用于用添加的功能包装视图。视图派生器与 decorator 参数 pyramid.config.Configurator.add_view() ,但它们可以为应用程序中的每个视图执行。

想一想 view deriver 作为视图的中间件。与tweens或wsgi中间件不同的是,视图派生器在应用程序的每个视图中调用一次,并且可以使用视图中的配置选项自定义其行为。

内置视图派生器

有几个内置的视图派生器 Pyramid 将自动应用于任何视图。在下面,它们是按照从最远到最接近用户定义的顺序定义的。 view callable

secured_view

强制执行 permission 在视图上定义。如果未定义权限,则此元素为no op。注意:如果通过 pyramid.config.Configurator.set_default_permission() 除非视图是 exception view .

此元素还将在以下情况下输出有用的调试信息: pyramid.debug_authorization 启用。

csrf_view

用于检查请求中提供的CSRF令牌。此元素是一个no op if require_csrf 视图选项不是 True . 注意,总会有一个 require_csrf 如果默认值通过 pyramid.config.Configurator.set_default_csrf_options() 除非视图是 exception view .

owrapped_view

调用由 wrapper 选择权。

http_cached_view

将缓存控制头应用于由 http_cache 选择权。如果 pyramid.prevent_http_cache 设置被启用或 http_cache 选项是 None .

decorated_view

将视图与来自 decorator 选择权。

rendered_view

调整的结果 view callable 变成一个 response 对象。在这一点之下,结果可能是任何python对象。

mapped_view

适用于 view mapper 定义为 mapper 选项或应用程序的默认视图映射器 view callable . 这始终是最接近用户定义视图的派生器,并将视图管道接口标准化以接受 (context, request) 从所有以前的视图派生。

警告

定义的任何视图派生器 under 这个 rendered_view 不能保证收到有效的响应对象。相反,他们将从 view mapper 这可能是视图返回的原始响应。这可能是一个渲染器的字典,但它可能是任何可以适应响应的Python对象。

自定义视图派生器

可以定义自定义视图派生器,它将影响应用程序中的所有视图。这有许多用途,但大多数都将集中在监视和安全上。为了注册一个自定义 view deriver ,您应该创建一个符合 pyramid.interfaces.IViewDeriver 接口,然后使用 pyramid.config.Configurator.add_view_deriver() . 可调用的应接受 view 被包裹和 info 对象,它是 pyramid.interfaces.IViewDeriverInfo . 例如,下面是一个可调用的,它可以为视图管道提供时间信息:

 1import time
 2
 3def timing_view(view, info):
 4    if info.options.get('timed'):
 5        def wrapper_view(context, request):
 6            start = time.time()
 7            response = view(context, request)
 8            end = time.time()
 9            response.headers['X-View-Performance'] = '%.3f' % (end - start,)
10            return response
11        return wrapper_view
12    return view
13
14timing_view.options = ('timed',)
15
16config.add_view_deriver(timing_view)

设置 timed 在时间上,视图表示金字塔 timed 是有效的 view_config 关键字参数。这个 timing_view 上面注册的自定义视图派生器将仅对使用 timed=True 作为其一个值传递的值 view_config 关键词。

例如,此视图配置将 not 成为定时视图:

1@view_config(route_name='home')
2def home(request):
3    return Response('Home')

但这种观点 will 将计时信息添加到响应头:

1@view_config(route_name='home', timed=True)
2def home(request):
3    return Response('Home')

视图派生器是唯一的,因为它们可以访问传递给 pyramid.config.Configurator.add_view() 为了决定要做什么,它们有机会影响应用程序中的每个视图。

异常视图和视图派生器

A view deriver 有机会包装任何视图,包括 exception view . 一般来说,这是可以的,但是某些视图派生器可能希望在处理异常时避免做某些事情。例如, csrf_viewsecured_view 除非明确要求,否则内置视图派生器不会对异常视图执行安全检查。

你可以查一下 info.exception_onlypyramid.interfaces.IViewDeriverInfo 对象包装视图以确定是包装异常视图还是普通视图。

排序视图派生器

默认情况下,在 decorated_viewrendered_view 内置派生器。可以使用 overunder 选项。每个选项都可以使用其他视图派生器的名称来指定顺序。很少有理由担心派生器的顺序,除非派生器依赖于视图管道中的其他操作。

两个 overunder 也可能是不受约束的。对于任一选项,如果定义了一个或多个约束,则必须至少满足一个约束,否则 pyramid.exceptions.ConfigurationError 将被提升。如果缺少另一个派生器,则可以使用此函数定义回退约束。

存在两个sentinel值, pyramid.viewderivers.INGRESSpyramid.viewderivers.VIEW ,可以在视图管道的边缘指定约束时使用。例如,要在管道开始处添加派生器,可以使用 under=INGRESS .

无法在 mapped_view 作为 view mapper 与用户定义的签名密切相关 view callable . 如果您只需要知道原始视图可调用的是什么,它可以被发现为 info.original_view 论提供 pyramid.interfaces.IViewDeriverInfo 对象传递给每个视图派生器。

警告

任何视图派生器的默认约束为 over='rendered_view'under='decorated_view' . 在转义这些约束时,必须注意避免派生器之间的循环依赖关系。例如,如果要在 secured_view 然后简单说明 over='secured_view' 是不够的,因为默认值也低于 decorated view 会有一个不令人满意的周期。必须指定有效的 under 约束,例如 under=INGRESS 在入口和 secured_view 在视图管道的开头。