commit | author | age
|
c8cf22
|
1 |
.. _hooks_chapter: |
CM |
2 |
|
13c923
|
3 |
Using Hooks |
CM |
4 |
=========== |
c8cf22
|
5 |
|
e1a7e0
|
6 |
"Hooks" can be used to influence the behavior of the :app:`Pyramid` framework |
CM |
7 |
in various ways. |
fab8c5
|
8 |
|
8c56ae
|
9 |
.. index:: |
c5f24b
|
10 |
single: not found view |
8c56ae
|
11 |
|
7bc20e
|
12 |
.. _changing_the_notfound_view: |
CM |
13 |
|
fab8c5
|
14 |
Changing the Not Found View |
CM |
15 |
--------------------------- |
|
16 |
|
8aa1c2
|
17 |
When :app:`Pyramid` can't map a URL to view code, it invokes a :term:`Not Found |
SP |
18 |
View`, which is a :term:`view callable`. The default Not Found View can be |
|
19 |
overridden through application configuration. |
ff1213
|
20 |
|
8aa1c2
|
21 |
If your application uses :term:`imperative configuration`, you can replace the |
SP |
22 |
Not Found View by using the |
0db4a1
|
23 |
:meth:`pyramid.config.Configurator.add_notfound_view` method: |
0ac818
|
24 |
|
e1a7e0
|
25 |
.. code-block:: python |
CM |
26 |
:linenos: |
0ac818
|
27 |
|
439a02
|
28 |
def notfound(request): |
c898dd
|
29 |
return Response('Not Found', status='404 Not Found') |
0ac818
|
30 |
|
439a02
|
31 |
def main(globals, **settings): |
TL |
32 |
config = Configurator() |
|
33 |
config.add_notfound_view(notfound) |
|
34 |
|
|
35 |
The :term:`Not Found View` callable is a view callable like any other. |
0db4a1
|
36 |
|
CM |
37 |
If your application instead uses :class:`pyramid.view.view_config` decorators |
439a02
|
38 |
and a :term:`scan`, you can replace the Not Found View by using the |
0db4a1
|
39 |
:class:`pyramid.view.notfound_view_config` decorator: |
CM |
40 |
|
|
41 |
.. code-block:: python |
|
42 |
:linenos: |
|
43 |
|
|
44 |
from pyramid.view import notfound_view_config |
|
45 |
|
25d7c2
|
46 |
@notfound_view_config() |
0db4a1
|
47 |
def notfound(request): |
c898dd
|
48 |
return Response('Not Found', status='404 Not Found') |
0db4a1
|
49 |
|
CM |
50 |
def main(globals, **settings): |
439a02
|
51 |
config = Configurator() |
TL |
52 |
config.scan() |
0db4a1
|
53 |
|
CM |
54 |
This does exactly what the imperative example above showed. |
|
55 |
|
2f4bde
|
56 |
Your application can define *multiple* Not Found Views if necessary. Both |
0db4a1
|
57 |
:meth:`pyramid.config.Configurator.add_notfound_view` and |
CM |
58 |
:class:`pyramid.view.notfound_view_config` take most of the same arguments as |
|
59 |
:class:`pyramid.config.Configurator.add_view` and |
2f4bde
|
60 |
:class:`pyramid.view.view_config`, respectively. This means that Not Found |
TL |
61 |
Views can carry predicates limiting their applicability. For example: |
0db4a1
|
62 |
|
CM |
63 |
.. code-block:: python |
|
64 |
:linenos: |
|
65 |
|
|
66 |
from pyramid.view import notfound_view_config |
|
67 |
|
25d7c2
|
68 |
@notfound_view_config(request_method='GET') |
0db4a1
|
69 |
def notfound_get(request): |
c898dd
|
70 |
return Response('Not Found during GET', status='404 Not Found') |
0db4a1
|
71 |
|
25d7c2
|
72 |
@notfound_view_config(request_method='POST') |
0db4a1
|
73 |
def notfound_post(request): |
c898dd
|
74 |
return Response('Not Found during POST', status='404 Not Found') |
0db4a1
|
75 |
|
CM |
76 |
def main(globals, **settings): |
|
77 |
config = Configurator() |
|
78 |
config.scan() |
|
79 |
|
8aa1c2
|
80 |
The ``notfound_get`` view will be called when a view could not be found and the |
SP |
81 |
request method was ``GET``. The ``notfound_post`` view will be called when a |
|
82 |
view could not be found and the request method was ``POST``. |
0ac818
|
83 |
|
cec2b0
|
84 |
Like any other view, the Not Found View must accept at least a ``request`` |
8aa1c2
|
85 |
parameter, or both ``context`` and ``request``. The ``request`` is the current |
SP |
86 |
:term:`request` representing the denied action. The ``context`` (if used in |
|
87 |
the call signature) will be the instance of the |
99edc5
|
88 |
:exc:`~pyramid.httpexceptions.HTTPNotFound` exception that caused the view to |
CM |
89 |
be called. |
fab8c5
|
90 |
|
0db4a1
|
91 |
Both :meth:`pyramid.config.Configurator.add_notfound_view` and |
CM |
92 |
:class:`pyramid.view.notfound_view_config` can be used to automatically |
|
93 |
redirect requests to slash-appended routes. See |
|
94 |
:ref:`redirecting_to_slash_appended_routes` for examples. |
|
95 |
|
cec2b0
|
96 |
Here's some sample code that implements a minimal :term:`Not Found View` |
TL |
97 |
callable: |
fab8c5
|
98 |
|
CM |
99 |
.. code-block:: python |
601289
|
100 |
:linenos: |
fab8c5
|
101 |
|
94b889
|
102 |
from pyramid.httpexceptions import HTTPNotFound |
fab8c5
|
103 |
|
0db4a1
|
104 |
def notfound(request): |
fab8c5
|
105 |
return HTTPNotFound() |
CM |
106 |
|
012b97
|
107 |
.. note:: |
M |
108 |
|
8aa1c2
|
109 |
When a Not Found View callable is invoked, it is passed a :term:`request`. |
SP |
110 |
The ``exception`` attribute of the request will be an instance of the |
|
111 |
:exc:`~pyramid.httpexceptions.HTTPNotFound` exception that caused the Not |
|
112 |
Found View to be called. The value of ``request.exception.message`` will be |
|
113 |
a value explaining why the Not Found exception was raised. This message has |
|
114 |
different values depending on whether the ``pyramid.debug_notfound`` |
|
115 |
environment setting is true or false. |
ff1213
|
116 |
|
0db4a1
|
117 |
.. note:: |
CM |
118 |
|
8aa1c2
|
119 |
When a Not Found View callable accepts an argument list as described in |
SP |
120 |
:ref:`request_and_context_view_definitions`, the ``context`` passed as the |
|
121 |
first argument to the view callable will be the |
99edc5
|
122 |
:exc:`~pyramid.httpexceptions.HTTPNotFound` exception instance. If |
CM |
123 |
available, the resource context will still be available as |
|
124 |
``request.context``. |
f6fb4b
|
125 |
|
MM |
126 |
.. warning:: |
|
127 |
|
|
128 |
The :term:`Not Found View` callables are only invoked when a |
|
129 |
:exc:`~pyramid.httpexceptions.HTTPNotFound` exception is raised. If the |
|
130 |
exception is returned from a view then it will be treated as a regular |
|
131 |
response object and it will not trigger the custom view. |
8c56ae
|
132 |
|
CM |
133 |
.. index:: |
c5f24b
|
134 |
single: forbidden view |
fab8c5
|
135 |
|
7bc20e
|
136 |
.. _changing_the_forbidden_view: |
CM |
137 |
|
fab8c5
|
138 |
Changing the Forbidden View |
CM |
139 |
--------------------------- |
|
140 |
|
e1a7e0
|
141 |
When :app:`Pyramid` can't authorize execution of a view based on the |
8aa1c2
|
142 |
:term:`authorization policy` in use, it invokes a :term:`forbidden view`. The |
SP |
143 |
default forbidden response has a 403 status code and is very plain, but the |
|
144 |
view which generates it can be overridden as necessary. |
ff1213
|
145 |
|
e1a7e0
|
146 |
The :term:`forbidden view` callable is a view callable like any other. The |
8aa1c2
|
147 |
:term:`view configuration` which causes it to be a "forbidden" view consists of |
SP |
148 |
using the :meth:`pyramid.config.Configurator.add_forbidden_view` API or the |
a7fe30
|
149 |
:class:`pyramid.view.forbidden_view_config` decorator. |
0ac818
|
150 |
|
a7fe30
|
151 |
For example, you can add a forbidden view by using the |
CM |
152 |
:meth:`pyramid.config.Configurator.add_forbidden_view` method to register a |
|
153 |
forbidden view: |
0ac818
|
154 |
|
e1a7e0
|
155 |
.. code-block:: python |
CM |
156 |
:linenos: |
0ac818
|
157 |
|
439a02
|
158 |
def forbidden(request): |
TL |
159 |
return Response('forbidden') |
0ac818
|
160 |
|
439a02
|
161 |
def main(globals, **settings): |
TL |
162 |
config = Configurator() |
f9c61b
|
163 |
config.add_forbidden_view(forbidden) |
0ac818
|
164 |
|
a7fe30
|
165 |
If instead you prefer to use decorators and a :term:`scan`, you can use the |
CM |
166 |
:class:`pyramid.view.forbidden_view_config` decorator to mark a view callable |
|
167 |
as a forbidden view: |
|
168 |
|
|
169 |
.. code-block:: python |
|
170 |
:linenos: |
|
171 |
|
|
172 |
from pyramid.view import forbidden_view_config |
|
173 |
|
bce621
|
174 |
@forbidden_view_config() |
a7fe30
|
175 |
def forbidden(request): |
CM |
176 |
return Response('forbidden') |
|
177 |
|
|
178 |
def main(globals, **settings): |
|
179 |
config = Configurator() |
|
180 |
config.scan() |
|
181 |
|
e1a7e0
|
182 |
Like any other view, the forbidden view must accept at least a ``request`` |
8aa1c2
|
183 |
parameter, or both ``context`` and ``request``. If a forbidden view callable |
SP |
184 |
accepts both ``context`` and ``request``, the HTTP Exception is passed as |
|
185 |
context. The ``context`` as found by the router when the view was denied (which |
|
186 |
you normally would expect) is available as ``request.context``. The |
|
187 |
``request`` is the current :term:`request` representing the denied action. |
13c923
|
188 |
|
CM |
189 |
Here's some sample code that implements a minimal forbidden view: |
fab8c5
|
190 |
|
CM |
191 |
.. code-block:: python |
601289
|
192 |
:linenos: |
fab8c5
|
193 |
|
02d745
|
194 |
from pyramid.view import view_config |
e1a7e0
|
195 |
from pyramid.response import Response |
fab8c5
|
196 |
|
6103bf
|
197 |
def forbidden_view(request): |
e1a7e0
|
198 |
return Response('forbidden') |
fab8c5
|
199 |
|
012b97
|
200 |
.. note:: |
M |
201 |
|
8aa1c2
|
202 |
When a forbidden view callable is invoked, it is passed a :term:`request`. |
SP |
203 |
The ``exception`` attribute of the request will be an instance of the |
|
204 |
:exc:`~pyramid.httpexceptions.HTTPForbidden` exception that caused the |
|
205 |
forbidden view to be called. The value of ``request.exception.message`` |
|
206 |
will be a value explaining why the forbidden exception was raised, and |
|
207 |
``request.exception.result`` will be extended information about the |
|
208 |
forbidden exception. These messages have different values depending on |
|
209 |
whether the ``pyramid.debug_authorization`` environment setting is true or |
|
210 |
false. |
fab8c5
|
211 |
|
f6fb4b
|
212 |
.. warning:: |
MM |
213 |
|
|
214 |
The :term:`forbidden view` callables are only invoked when a |
|
215 |
:exc:`~pyramid.httpexceptions.HTTPForbidden` exception is raised. If the |
|
216 |
exception is returned from a view then it will be treated as a regular |
|
217 |
response object and it will not trigger the custom view. |
|
218 |
|
8c56ae
|
219 |
.. index:: |
74abc0
|
220 |
single: request factory |
CM |
221 |
|
|
222 |
.. _changing_the_request_factory: |
|
223 |
|
|
224 |
Changing the Request Factory |
|
225 |
---------------------------- |
|
226 |
|
ee9769
|
227 |
Whenever :app:`Pyramid` handles a request from a :term:`WSGI` server, it |
CM |
228 |
creates a :term:`request` object based on the WSGI environment it has been |
8aa1c2
|
229 |
passed. By default, an instance of the :class:`pyramid.request.Request` class |
SP |
230 |
is created to represent the request object. |
74abc0
|
231 |
|
8aa1c2
|
232 |
The class (a.k.a., "factory") that :app:`Pyramid` uses to create a request |
SP |
233 |
object instance can be changed by passing a ``request_factory`` argument to the |
74abc0
|
234 |
constructor of the :term:`configurator`. This argument can be either a |
CM |
235 |
callable or a :term:`dotted Python name` representing a callable. |
|
236 |
|
|
237 |
.. code-block:: python |
|
238 |
:linenos: |
|
239 |
|
|
240 |
from pyramid.request import Request |
|
241 |
|
|
242 |
class MyRequest(Request): |
|
243 |
pass |
|
244 |
|
|
245 |
config = Configurator(request_factory=MyRequest) |
|
246 |
|
|
247 |
If you're doing imperative configuration, and you'd rather do it after you've |
8aa1c2
|
248 |
already constructed a :term:`configurator`, it can also be registered via the |
74abc0
|
249 |
:meth:`pyramid.config.Configurator.set_request_factory` method: |
CM |
250 |
|
|
251 |
.. code-block:: python |
|
252 |
:linenos: |
|
253 |
|
|
254 |
from pyramid.config import Configurator |
|
255 |
from pyramid.request import Request |
|
256 |
|
|
257 |
class MyRequest(Request): |
|
258 |
pass |
|
259 |
|
|
260 |
config = Configurator() |
|
261 |
config.set_request_factory(MyRequest) |
|
262 |
|
|
263 |
.. index:: |
9db1a1
|
264 |
single: request method |
K |
265 |
|
|
266 |
.. _adding_request_method: |
|
267 |
|
8aa1c2
|
268 |
Adding Methods or Properties to a Request Object |
SP |
269 |
------------------------------------------------ |
9db1a1
|
270 |
|
d49c69
|
271 |
.. versionadded:: 1.4 |
5b9c21
|
272 |
|
K |
273 |
Since each Pyramid application can only have one :term:`request` factory, |
8aa1c2
|
274 |
:ref:`changing the request factory <changing_the_request_factory>` is not that |
SP |
275 |
extensible, especially if you want to build composable features (e.g., Pyramid |
|
276 |
add-ons and plugins). |
9db1a1
|
277 |
|
K |
278 |
A lazy property can be registered to the request object via the |
8aa1c2
|
279 |
:meth:`pyramid.config.Configurator.add_request_method` API. This allows you to |
SP |
280 |
specify a callable that will be available on the request object, but will not |
9db1a1
|
281 |
actually execute the function until accessed. |
K |
282 |
|
c7a06f
|
283 |
.. warning:: |
CM |
284 |
|
|
285 |
This will silently override methods and properties from :term:`request |
|
286 |
factory` that have the same name. |
9db1a1
|
287 |
|
K |
288 |
.. code-block:: python |
|
289 |
:linenos: |
|
290 |
|
|
291 |
from pyramid.config import Configurator |
|
292 |
|
|
293 |
def total(request, *args): |
|
294 |
return sum(args) |
|
295 |
|
|
296 |
def prop(request): |
edfc4f
|
297 |
print("getting the property") |
9db1a1
|
298 |
return "the property" |
K |
299 |
|
|
300 |
config = Configurator() |
|
301 |
config.add_request_method(total) |
|
302 |
config.add_request_method(prop, reify=True) |
|
303 |
|
8aa1c2
|
304 |
In the above example, ``total`` is added as a method. However, ``prop`` is |
SP |
305 |
added as a property and its result is cached per-request by setting |
|
306 |
``reify=True``. This way, we eliminate the overhead of running the function |
|
307 |
multiple times. |
9db1a1
|
308 |
|
a470ff
|
309 |
.. testsetup:: group1 |
SP |
310 |
|
|
311 |
from pyramid.config import Configurator |
|
312 |
|
|
313 |
|
|
314 |
def total(request, *args): |
|
315 |
return sum(args) |
|
316 |
|
|
317 |
|
|
318 |
def prop(request): |
|
319 |
print("getting the property") |
|
320 |
return "the property" |
|
321 |
|
|
322 |
|
|
323 |
|
|
324 |
config = Configurator() |
|
325 |
config.add_request_method(total) |
|
326 |
config.add_request_method(prop, reify=True) |
|
327 |
config.commit() |
|
328 |
|
|
329 |
from pyramid.scripting import prepare |
|
330 |
request = prepare(registry=config.registry)["request"] |
|
331 |
|
|
332 |
.. doctest:: group1 |
|
333 |
|
9db1a1
|
334 |
>>> request.total(1, 2, 3) |
K |
335 |
6 |
|
336 |
>>> request.prop |
|
337 |
getting the property |
a470ff
|
338 |
'the property' |
9db1a1
|
339 |
>>> request.prop |
a470ff
|
340 |
'the property' |
9db1a1
|
341 |
|
K |
342 |
To not cache the result of ``request.prop``, set ``property=True`` instead of |
|
343 |
``reify=True``. |
|
344 |
|
|
345 |
Here is an example of passing a class to ``Configurator.add_request_method``: |
|
346 |
|
|
347 |
.. code-block:: python |
|
348 |
:linenos: |
|
349 |
|
|
350 |
from pyramid.config import Configurator |
|
351 |
from pyramid.decorator import reify |
|
352 |
|
|
353 |
class ExtraStuff(object): |
|
354 |
|
|
355 |
def __init__(self, request): |
|
356 |
self.request = request |
|
357 |
|
|
358 |
def total(self, *args): |
|
359 |
return sum(args) |
|
360 |
|
|
361 |
# use @property if you don't want to cache the result |
|
362 |
@reify |
|
363 |
def prop(self): |
edfc4f
|
364 |
print("getting the property") |
9db1a1
|
365 |
return "the property" |
K |
366 |
|
|
367 |
config = Configurator() |
|
368 |
config.add_request_method(ExtraStuff, 'extra', reify=True) |
|
369 |
|
|
370 |
We attach and cache an object named ``extra`` to the ``request`` object. |
|
371 |
|
a470ff
|
372 |
.. testsetup:: group2 |
SP |
373 |
|
|
374 |
from pyramid.config import Configurator |
|
375 |
from pyramid.decorator import reify |
|
376 |
|
|
377 |
class ExtraStuff(object): |
|
378 |
|
|
379 |
def __init__(self, request): |
|
380 |
self.request = request |
|
381 |
|
|
382 |
def total(self, *args): |
|
383 |
return sum(args) |
|
384 |
|
|
385 |
# use @property if you don't want to cache the result |
|
386 |
@reify |
|
387 |
def prop(self): |
|
388 |
print("getting the property") |
|
389 |
return "the property" |
|
390 |
|
|
391 |
config = Configurator() |
|
392 |
config.add_request_method(ExtraStuff, 'extra', reify=True) |
|
393 |
config.commit() |
|
394 |
|
|
395 |
from pyramid.scripting import prepare |
|
396 |
request = prepare(registry=config.registry)["request"] |
|
397 |
|
|
398 |
.. doctest:: group2 |
|
399 |
|
9db1a1
|
400 |
>>> request.extra.total(1, 2, 3) |
K |
401 |
6 |
|
402 |
>>> request.extra.prop |
|
403 |
getting the property |
a470ff
|
404 |
'the property' |
9db1a1
|
405 |
>>> request.extra.prop |
a470ff
|
406 |
'the property' |
SP |
407 |
|
9db1a1
|
408 |
|
K |
409 |
.. index:: |
1236de
|
410 |
single: response factory |
JA |
411 |
|
|
412 |
.. _changing_the_response_factory: |
|
413 |
|
|
414 |
Changing the Response Factory |
8aa1c2
|
415 |
----------------------------- |
1236de
|
416 |
|
807e94
|
417 |
.. versionadded:: 1.6 |
JA |
418 |
|
8aa1c2
|
419 |
Whenever :app:`Pyramid` returns a response from a view, it creates a |
1236de
|
420 |
:term:`response` object. By default, an instance of the |
JA |
421 |
:class:`pyramid.response.Response` class is created to represent the response |
|
422 |
object. |
|
423 |
|
8aa1c2
|
424 |
The factory that :app:`Pyramid` uses to create a response object instance can |
SP |
425 |
be changed by passing a :class:`pyramid.interfaces.IResponseFactory` argument |
|
426 |
to the constructor of the :term:`configurator`. This argument can be either a |
da5f5f
|
427 |
callable or a :term:`dotted Python name` representing a callable. |
1236de
|
428 |
|
5de795
|
429 |
The factory takes a single positional argument, which is a :term:`Request` |
da5f5f
|
430 |
object. The argument may be ``None``. |
5de795
|
431 |
|
1236de
|
432 |
.. code-block:: python |
JA |
433 |
:linenos: |
|
434 |
|
|
435 |
from pyramid.response import Response |
|
436 |
|
|
437 |
class MyResponse(Response): |
|
438 |
pass |
|
439 |
|
32cb80
|
440 |
config = Configurator(response_factory=lambda r: MyResponse()) |
1236de
|
441 |
|
8aa1c2
|
442 |
If you're doing imperative configuration and you'd rather do it after you've |
SP |
443 |
already constructed a :term:`configurator`, it can also be registered via the |
1236de
|
444 |
:meth:`pyramid.config.Configurator.set_response_factory` method: |
JA |
445 |
|
|
446 |
.. code-block:: python |
|
447 |
:linenos: |
|
448 |
|
|
449 |
from pyramid.config import Configurator |
|
450 |
from pyramid.response import Response |
|
451 |
|
|
452 |
class MyResponse(Response): |
|
453 |
pass |
|
454 |
|
|
455 |
config = Configurator() |
32cb80
|
456 |
config.set_response_factory(lambda r: MyResponse()) |
1236de
|
457 |
|
8b5000
|
458 |
.. index:: |
SP |
459 |
single: before render event |
|
460 |
single: adding renderer globals |
|
461 |
|
|
462 |
.. _beforerender_event: |
1236de
|
463 |
|
8aa1c2
|
464 |
Using the Before Render Event |
ac7a9a
|
465 |
----------------------------- |
CM |
466 |
|
|
467 |
Subscribers to the :class:`pyramid.events.BeforeRender` event may introspect |
|
468 |
and modify the set of :term:`renderer globals` before they are passed to a |
8aa1c2
|
469 |
:term:`renderer`. This event object iself has a dictionary-like interface that |
SP |
470 |
can be used for this purpose. For example: |
ac7a9a
|
471 |
|
CM |
472 |
.. code-block:: python |
392a6c
|
473 |
:linenos: |
ac7a9a
|
474 |
|
CM |
475 |
from pyramid.events import subscriber |
|
476 |
from pyramid.events import BeforeRender |
|
477 |
|
|
478 |
@subscriber(BeforeRender) |
|
479 |
def add_global(event): |
|
480 |
event['mykey'] = 'foo' |
|
481 |
|
8aa1c2
|
482 |
An object of this type is sent as an event just before a :term:`renderer` is |
SP |
483 |
invoked. |
ac7a9a
|
484 |
|
8aa1c2
|
485 |
If a subscriber attempts to add a key that already exists in the renderer |
ac7a9a
|
486 |
globals dictionary, a :exc:`KeyError` is raised. This limitation is enforced |
CM |
487 |
because event subscribers do not possess any relative ordering. The set of |
|
488 |
keys added to the renderer globals dictionary by all |
8aa1c2
|
489 |
:class:`pyramid.events.BeforeRender` subscribers and renderer globals factories |
SP |
490 |
must be unique. |
ac7a9a
|
491 |
|
a31924
|
492 |
The dictionary returned from the view is accessible through the |
JC |
493 |
:attr:`rendering_val` attribute of a :class:`~pyramid.events.BeforeRender` |
2516df
|
494 |
event. |
JC |
495 |
|
8aa1c2
|
496 |
Suppose you return ``{'mykey': 'somevalue', 'mykey2': 'somevalue2'}`` from your |
SP |
497 |
view callable, like so: |
a31924
|
498 |
|
JC |
499 |
.. code-block:: python |
2516df
|
500 |
:linenos: |
a31924
|
501 |
|
2516df
|
502 |
from pyramid.view import view_config |
a31924
|
503 |
|
2516df
|
504 |
@view_config(renderer='some_renderer') |
JC |
505 |
def myview(request): |
|
506 |
return {'mykey': 'somevalue', 'mykey2': 'somevalue2'} |
|
507 |
|
|
508 |
:attr:`rendering_val` can be used to access these values from the |
|
509 |
:class:`~pyramid.events.BeforeRender` object: |
|
510 |
|
|
511 |
.. code-block:: python |
|
512 |
:linenos: |
|
513 |
|
|
514 |
from pyramid.events import subscriber |
|
515 |
from pyramid.events import BeforeRender |
|
516 |
|
|
517 |
@subscriber(BeforeRender) |
|
518 |
def read_return(event): |
|
519 |
# {'mykey': 'somevalue'} is returned from the view |
|
520 |
print(event.rendering_val['mykey']) |
a31924
|
521 |
|
ac7a9a
|
522 |
See the API documentation for the :class:`~pyramid.events.BeforeRender` event |
CM |
523 |
interface at :class:`pyramid.interfaces.IBeforeRender`. |
74abc0
|
524 |
|
CM |
525 |
.. index:: |
|
526 |
single: response callback |
|
527 |
|
|
528 |
.. _using_response_callbacks: |
|
529 |
|
|
530 |
Using Response Callbacks |
|
531 |
------------------------ |
|
532 |
|
|
533 |
Unlike many other web frameworks, :app:`Pyramid` does not eagerly create a |
|
534 |
global response object. Adding a :term:`response callback` allows an |
f9896b
|
535 |
application to register an action to be performed against whatever response |
CM |
536 |
object is returned by a view, usually in order to mutate the response. |
74abc0
|
537 |
|
CM |
538 |
The :meth:`pyramid.request.Request.add_response_callback` method is used to |
|
539 |
register a response callback. |
|
540 |
|
|
541 |
A response callback is a callable which accepts two positional parameters: |
|
542 |
``request`` and ``response``. For example: |
|
543 |
|
|
544 |
.. code-block:: python |
|
545 |
:linenos: |
|
546 |
|
|
547 |
def cache_callback(request, response): |
|
548 |
"""Set the cache_control max_age for the response""" |
|
549 |
if request.exception is not None: |
|
550 |
response.cache_control.max_age = 360 |
|
551 |
request.add_response_callback(cache_callback) |
|
552 |
|
8aa1c2
|
553 |
No response callback is called if an unhandled exception happens in application |
SP |
554 |
code, or if the response object returned by a :term:`view callable` is invalid. |
|
555 |
Response callbacks *are*, however, invoked when a :term:`exception view` is |
|
556 |
rendered successfully. In such a case, the :attr:`request.exception` attribute |
|
557 |
of the request when it enters a response callback will be an exception object |
|
558 |
instead of its default value of ``None``. |
74abc0
|
559 |
|
CM |
560 |
Response callbacks are called in the order they're added |
8aa1c2
|
561 |
(first-to-most-recently-added). All response callbacks are called *before* the |
SP |
562 |
:class:`~pyramid.events.NewResponse` event is sent. Errors raised by response |
|
563 |
callbacks are not handled specially. They will be propagated to the caller of |
|
564 |
the :app:`Pyramid` router application. |
74abc0
|
565 |
|
CM |
566 |
A response callback has a lifetime of a *single* request. If you want a |
|
567 |
response callback to happen as the result of *every* request, you must |
8aa1c2
|
568 |
re-register the callback into every new request (perhaps within a subscriber of |
SP |
569 |
a :class:`~pyramid.events.NewRequest` event). |
74abc0
|
570 |
|
CM |
571 |
.. index:: |
|
572 |
single: finished callback |
|
573 |
|
|
574 |
.. _using_finished_callbacks: |
|
575 |
|
|
576 |
Using Finished Callbacks |
|
577 |
------------------------ |
|
578 |
|
|
579 |
A :term:`finished callback` is a function that will be called unconditionally |
8aa1c2
|
580 |
by the :app:`Pyramid` :term:`router` at the very end of request processing. A |
SP |
581 |
finished callback can be used to perform an action at the end of a request |
74abc0
|
582 |
unconditionally. |
CM |
583 |
|
|
584 |
The :meth:`pyramid.request.Request.add_finished_callback` method is used to |
|
585 |
register a finished callback. |
|
586 |
|
8aa1c2
|
587 |
A finished callback is a callable which accepts a single positional parameter: |
SP |
588 |
``request``. For example: |
74abc0
|
589 |
|
CM |
590 |
.. code-block:: python |
|
591 |
:linenos: |
|
592 |
|
ae6d08
|
593 |
import logging |
74abc0
|
594 |
|
ae6d08
|
595 |
log = logging.getLogger(__name__) |
RS |
596 |
|
|
597 |
def log_callback(request): |
|
598 |
"""Log information at the end of request""" |
|
599 |
log.debug('Request is finished.') |
|
600 |
request.add_finished_callback(log_callback) |
74abc0
|
601 |
|
4dc751
|
602 |
Finished callbacks are called in the order they're added |
8aa1c2
|
603 |
(first-to-most-recently-added). Finished callbacks (unlike a :term:`response |
SP |
604 |
callback`) are *always* called, even if an exception happens in application |
|
605 |
code that prevents a response from being generated. |
74abc0
|
606 |
|
8aa1c2
|
607 |
The set of finished callbacks associated with a request are called *very late* |
SP |
608 |
in the processing of that request; they are essentially the very last thing |
|
609 |
called by the :term:`router` before a request "ends". They are called after |
|
610 |
response processing has already occurred in a top-level ``finally:`` block |
|
611 |
within the router request processing code. As a result, mutations performed to |
|
612 |
the ``request`` provided to a finished callback will have no meaningful effect, |
|
613 |
because response processing will have already occurred, and the request's scope |
|
614 |
will expire almost immediately after all finished callbacks have been |
|
615 |
processed. |
74abc0
|
616 |
|
8aa1c2
|
617 |
Errors raised by finished callbacks are not handled specially. They will be |
SP |
618 |
propagated to the caller of the :app:`Pyramid` router application. |
74abc0
|
619 |
|
CM |
620 |
A finished callback has a lifetime of a *single* request. If you want a |
|
621 |
finished callback to happen as the result of *every* request, you must |
8aa1c2
|
622 |
re-register the callback into every new request (perhaps within a subscriber of |
SP |
623 |
a :class:`~pyramid.events.NewRequest` event). |
8c56ae
|
624 |
|
CM |
625 |
.. index:: |
c5f24b
|
626 |
single: traverser |
def444
|
627 |
|
80a25e
|
628 |
.. _changing_the_traverser: |
CM |
629 |
|
|
630 |
Changing the Traverser |
|
631 |
---------------------- |
|
632 |
|
e1a7e0
|
633 |
The default :term:`traversal` algorithm that :app:`Pyramid` uses is explained |
CM |
634 |
in :ref:`traversal_algorithm`. Though it is rarely necessary, this default |
8aa1c2
|
635 |
algorithm can be swapped out selectively for a different traversal pattern via |
SP |
636 |
configuration. |
80a25e
|
637 |
|
e1a7e0
|
638 |
.. code-block:: python |
80a25e
|
639 |
:linenos: |
CM |
640 |
|
748aad
|
641 |
from pyramid.config import Configurator |
e1a7e0
|
642 |
from myapp.traversal import Traverser |
748aad
|
643 |
config = Configurator() |
13305e
|
644 |
config.add_traverser(Traverser) |
e1a7e0
|
645 |
|
CM |
646 |
In the example above, ``myapp.traversal.Traverser`` is assumed to be a class |
|
647 |
that implements the following interface: |
80a25e
|
648 |
|
CM |
649 |
.. code-block:: python |
|
650 |
:linenos: |
|
651 |
|
|
652 |
class Traverser(object): |
|
653 |
def __init__(self, root): |
|
654 |
""" Accept the root object returned from the root factory """ |
|
655 |
|
acc776
|
656 |
def __call__(self, request): |
80a25e
|
657 |
""" Return a dictionary with (at least) the keys ``root``, |
CM |
658 |
``context``, ``view_name``, ``subpath``, ``traversed``, |
|
659 |
``virtual_root``, and ``virtual_root_path``. These values are |
fb6a5c
|
660 |
typically the result of a resource tree traversal. ``root`` |
CM |
661 |
is the physical root object, ``context`` will be a resource |
80a25e
|
662 |
object, ``view_name`` will be the view name used (a Unicode |
CM |
663 |
name), ``subpath`` will be a sequence of Unicode names that |
|
664 |
followed the view name but were not traversed, ``traversed`` |
|
665 |
will be a sequence of Unicode names that were traversed |
|
666 |
(including the virtual root path, if any) ``virtual_root`` |
fb6a5c
|
667 |
will be a resource object representing the virtual root (or the |
80a25e
|
668 |
physical root if traversal was not performed), and |
CM |
669 |
``virtual_root_path`` will be a sequence representing the |
|
670 |
virtual root path (a sequence of Unicode names) or None if |
|
671 |
traversal was not performed. |
|
672 |
|
|
673 |
Extra keys for special purpose functionality can be added as |
|
674 |
necessary. |
|
675 |
|
|
676 |
All values returned in the dictionary will be made available |
|
677 |
as attributes of the ``request`` object. |
|
678 |
""" |
|
679 |
|
|
680 |
More than one traversal algorithm can be active at the same time. For |
e1a7e0
|
681 |
instance, if your :term:`root factory` returns more than one type of object |
8aa1c2
|
682 |
conditionally, you could claim that an alternative traverser adapter is "for" |
e1a7e0
|
683 |
only one particular class or interface. When the root factory returned an |
CM |
684 |
object that implemented that class or interface, a custom traverser would be |
8aa1c2
|
685 |
used. Otherwise the default traverser would be used. For example: |
80a25e
|
686 |
|
e1a7e0
|
687 |
.. code-block:: python |
80a25e
|
688 |
:linenos: |
CM |
689 |
|
e1a7e0
|
690 |
from myapp.traversal import Traverser |
CM |
691 |
from myapp.resources import MyRoot |
748aad
|
692 |
from pyramid.config import Configurator |
CM |
693 |
config = Configurator() |
13305e
|
694 |
config.add_traverser(Traverser, MyRoot) |
e1a7e0
|
695 |
|
CM |
696 |
If the above stanza was added to a Pyramid ``__init__.py`` file's ``main`` |
8aa1c2
|
697 |
function, :app:`Pyramid` would use the ``myapp.traversal.Traverser`` only when |
SP |
698 |
the application :term:`root factory` returned an instance of the |
fb6a5c
|
699 |
``myapp.resources.MyRoot`` object. Otherwise it would use the default |
fd5ae9
|
700 |
:app:`Pyramid` traverser to do traversal. |
80a25e
|
701 |
|
8c56ae
|
702 |
.. index:: |
8aa1c2
|
703 |
single: URL generator |
e1a7e0
|
704 |
|
CM |
705 |
.. _changing_resource_url: |
8c56ae
|
706 |
|
fb90f0
|
707 |
Changing How :meth:`pyramid.request.Request.resource_url` Generates a URL |
CM |
708 |
------------------------------------------------------------------------- |
80a25e
|
709 |
|
fb6a5c
|
710 |
When you add a traverser as described in :ref:`changing_the_traverser`, it's |
fb90f0
|
711 |
often convenient to continue to use the |
CM |
712 |
:meth:`pyramid.request.Request.resource_url` API. However, since the way |
8aa1c2
|
713 |
traversal is done will have been modified, the URLs it generates by default may |
SP |
714 |
be incorrect when used against resources derived from your custom traverser. |
80a25e
|
715 |
|
cb8e34
|
716 |
If you've added a traverser, you can change how |
fb90f0
|
717 |
:meth:`~pyramid.request.Request.resource_url` generates a URL for a specific |
c51896
|
718 |
type of resource by adding a call to |
125ea4
|
719 |
:meth:`pyramid.config.Configurator.add_resource_url_adapter`. |
c51896
|
720 |
|
CM |
721 |
For example: |
80a25e
|
722 |
|
e1a7e0
|
723 |
.. code-block:: python |
80a25e
|
724 |
:linenos: |
CM |
725 |
|
c51896
|
726 |
from myapp.traversal import ResourceURLAdapter |
e1a7e0
|
727 |
from myapp.resources import MyRoot |
80a25e
|
728 |
|
95129f
|
729 |
config.add_resource_url_adapter(ResourceURLAdapter, MyRoot) |
e1a7e0
|
730 |
|
8aa1c2
|
731 |
In the above example, the ``myapp.traversal.ResourceURLAdapter`` class will be |
SP |
732 |
used to provide services to :meth:`~pyramid.request.Request.resource_url` any |
|
733 |
time the :term:`resource` passed to ``resource_url`` is of the class |
c51896
|
734 |
``myapp.resources.MyRoot``. The ``resource_iface`` argument ``MyRoot`` |
CM |
735 |
represents the type of interface that must be possessed by the resource for |
|
736 |
this resource url factory to be found. If the ``resource_iface`` argument is |
8aa1c2
|
737 |
omitted, this resource URL adapter will be used for *all* resources. |
80a25e
|
738 |
|
e2b679
|
739 |
The API that must be implemented by a class that provides |
c51896
|
740 |
:class:`~pyramid.interfaces.IResourceURL` is as follows: |
80a25e
|
741 |
|
CM |
742 |
.. code-block:: python |
|
743 |
:linenos: |
|
744 |
|
c51896
|
745 |
class MyResourceURL(object): |
CM |
746 |
""" An adapter which provides the virtual and physical paths of a |
|
747 |
resource |
ca866f
|
748 |
""" |
c51896
|
749 |
def __init__(self, resource, request): |
8aa1c2
|
750 |
""" Accept the resource and request and set self.physical_path and |
SP |
751 |
self.virtual_path """ |
c51896
|
752 |
self.virtual_path = some_function_of(resource, request) |
44563d
|
753 |
self.virtual_path_tuple = some_function_of(resource, request) |
c51896
|
754 |
self.physical_path = some_other_function_of(resource, request) |
44563d
|
755 |
self.physical_path_tuple = some_function_of(resource, request) |
80a25e
|
756 |
|
e1a7e0
|
757 |
The default context URL generator is available for perusal as the class |
c51896
|
758 |
:class:`pyramid.traversal.ResourceURL` in the `traversal module |
cd8ac8
|
759 |
<https://github.com/Pylons/pyramid/blob/master/pyramid/traversal.py>`_ of the |
e1a7e0
|
760 |
:term:`Pylons` GitHub Pyramid repository. |
7aa7fd
|
761 |
|
8aa1c2
|
762 |
See :meth:`pyramid.config.Configurator.add_resource_url_adapter` for more |
SP |
763 |
information. |
c51896
|
764 |
|
74abc0
|
765 |
.. index:: |
d868ff
|
766 |
single: IResponse |
6ce1e0
|
767 |
single: special view responses |
df15ed
|
768 |
|
d868ff
|
769 |
.. _using_iresponse: |
df15ed
|
770 |
|
d868ff
|
771 |
Changing How Pyramid Treats View Responses |
CM |
772 |
------------------------------------------ |
df15ed
|
773 |
|
40dbf4
|
774 |
.. versionadded:: 1.1 |
TL |
775 |
|
d868ff
|
776 |
It is possible to control how Pyramid treats the result of calling a view |
CM |
777 |
callable on a per-type basis by using a hook involving |
078412
|
778 |
:meth:`pyramid.config.Configurator.add_response_adapter` or the |
MH |
779 |
:class:`~pyramid.response.response_adapter` decorator. |
df15ed
|
780 |
|
8aa1c2
|
781 |
Pyramid, in various places, adapts the result of calling a view callable to the |
SP |
782 |
:class:`~pyramid.interfaces.IResponse` interface to ensure that the object |
|
783 |
returned by the view callable is a "true" response object. The vast majority |
|
784 |
of time, the result of this adaptation is the result object itself, as view |
|
785 |
callables written by "civilians" who read the narrative documentation contained |
|
786 |
in this manual will always return something that implements the |
|
787 |
:class:`~pyramid.interfaces.IResponse` interface. Most typically, this will be |
|
788 |
an instance of the :class:`pyramid.response.Response` class or a subclass. If a |
|
789 |
civilian returns a non-Response object from a view callable that isn't |
|
790 |
configured to use a :term:`renderer`, they will typically expect the router to |
d868ff
|
791 |
raise an error. However, you can hook Pyramid in such a way that users can |
CM |
792 |
return arbitrary values from a view callable by providing an adapter which |
|
793 |
converts the arbitrary return value into something that implements |
|
794 |
:class:`~pyramid.interfaces.IResponse`. |
df15ed
|
795 |
|
d868ff
|
796 |
For example, if you'd like to allow view callables to return bare string |
8aa1c2
|
797 |
objects (without requiring a :term:`renderer` to convert a string to a response |
SP |
798 |
object), you can register an adapter which converts the string to a Response: |
df15ed
|
799 |
|
d868ff
|
800 |
.. code-block:: python |
CM |
801 |
:linenos: |
|
802 |
|
|
803 |
from pyramid.response import Response |
|
804 |
|
|
805 |
def string_response_adapter(s): |
|
806 |
response = Response(s) |
|
807 |
return response |
|
808 |
|
|
809 |
# config is an instance of pyramid.config.Configurator |
|
810 |
|
1a6fc7
|
811 |
config.add_response_adapter(string_response_adapter, str) |
d868ff
|
812 |
|
8aa1c2
|
813 |
Likewise, if you want to be able to return a simplified kind of response object |
SP |
814 |
from view callables, you can use the IResponse hook to register an adapter to |
|
815 |
the more complex IResponse interface: |
d868ff
|
816 |
|
CM |
817 |
.. code-block:: python |
|
818 |
:linenos: |
|
819 |
|
|
820 |
from pyramid.response import Response |
|
821 |
|
|
822 |
class SimpleResponse(object): |
|
823 |
def __init__(self, body): |
|
824 |
self.body = body |
|
825 |
|
|
826 |
def simple_response_adapter(simple_response): |
|
827 |
response = Response(simple_response.body) |
|
828 |
return response |
|
829 |
|
|
830 |
# config is an instance of pyramid.config.Configurator |
|
831 |
|
1a6fc7
|
832 |
config.add_response_adapter(simple_response_adapter, SimpleResponse) |
d868ff
|
833 |
|
CM |
834 |
If you want to implement your own Response object instead of using the |
|
835 |
:class:`pyramid.response.Response` object in any capacity at all, you'll have |
8aa1c2
|
836 |
to make sure that the object implements every attribute and method outlined in |
1ca5b3
|
837 |
:class:`pyramid.interfaces.IResponse` and you'll have to ensure that it uses |
3702ab
|
838 |
``zope.interface.implementer(IResponse)`` as a class decorator. |
d868ff
|
839 |
|
15ac15
|
840 |
.. code-block:: python |
MM |
841 |
:linenos: |
|
842 |
|
d868ff
|
843 |
from pyramid.interfaces import IResponse |
1ca5b3
|
844 |
from zope.interface import implementer |
d868ff
|
845 |
|
1ca5b3
|
846 |
@implementer(IResponse) |
d868ff
|
847 |
class MyResponse(object): |
8aa1c2
|
848 |
# ... an implementation of every method and attribute |
d868ff
|
849 |
# documented in IResponse should follow ... |
CM |
850 |
|
|
851 |
When an alternate response object implementation is returned by a view |
|
852 |
callable, if that object asserts that it implements |
|
853 |
:class:`~pyramid.interfaces.IResponse` (via |
1ca5b3
|
854 |
``zope.interface.implementer(IResponse)``) , an adapter needn't be registered |
d868ff
|
855 |
for the object; Pyramid will use it directly. |
CM |
856 |
|
|
857 |
An IResponse adapter for ``webob.Response`` (as opposed to |
|
858 |
:class:`pyramid.response.Response`) is registered by Pyramid by default at |
|
859 |
startup time, as by their nature, instances of this class (and instances of |
|
860 |
subclasses of the class) will natively provide IResponse. The adapter |
|
861 |
registered for ``webob.Response`` simply returns the response object. |
df15ed
|
862 |
|
8aa1c2
|
863 |
Instead of using :meth:`pyramid.config.Configurator.add_response_adapter`, you |
SP |
864 |
can use the :class:`pyramid.response.response_adapter` decorator: |
1f901a
|
865 |
|
CM |
866 |
.. code-block:: python |
|
867 |
:linenos: |
|
868 |
|
|
869 |
from pyramid.response import Response |
|
870 |
from pyramid.response import response_adapter |
|
871 |
|
|
872 |
@response_adapter(str) |
|
873 |
def string_response_adapter(s): |
|
874 |
response = Response(s) |
|
875 |
return response |
|
876 |
|
|
877 |
The above example, when scanned, has the same effect as: |
|
878 |
|
|
879 |
.. code-block:: python |
|
880 |
|
|
881 |
config.add_response_adapter(string_response_adapter, str) |
|
882 |
|
|
883 |
The :class:`~pyramid.response.response_adapter` decorator will have no effect |
|
884 |
until activated by a :term:`scan`. |
|
885 |
|
df15ed
|
886 |
.. index:: |
c5e83c
|
887 |
single: view mapper |
81a833
|
888 |
|
522d7d
|
889 |
.. _using_a_view_mapper: |
81a833
|
890 |
|
c5e83c
|
891 |
Using a View Mapper |
CM |
892 |
------------------- |
81a833
|
893 |
|
c5e83c
|
894 |
The default calling conventions for view callables are documented in the |
1130ec
|
895 |
:ref:`views_chapter` chapter. You can change the way users define view |
879bb5
|
896 |
callables by employing a :term:`view mapper`. |
c5e83c
|
897 |
|
CM |
898 |
A view mapper is an object that accepts a set of keyword arguments and which |
|
899 |
returns a callable. The returned callable is called with the :term:`view |
8aa1c2
|
900 |
callable` object. The returned callable should itself return another callable |
SP |
901 |
which can be called with the "internal calling protocol" ``(context, |
c5e83c
|
902 |
request)``. |
CM |
903 |
|
|
904 |
You can use a view mapper in a number of ways: |
|
905 |
|
8aa1c2
|
906 |
- by setting a ``__view_mapper__`` attribute (which is the view mapper object) |
SP |
907 |
on the view callable itself |
c5e83c
|
908 |
|
8aa1c2
|
909 |
- by passing the mapper object to :meth:`pyramid.config.Configurator.add_view` |
SP |
910 |
(or its declarative and decorator equivalents) as the ``mapper`` argument |
c5e83c
|
911 |
|
8aa1c2
|
912 |
- by registering a *default* view mapper |
c5e83c
|
913 |
|
CM |
914 |
Here's an example of a view mapper that emulates (somewhat) a Pylons |
|
915 |
"controller". The mapper is initialized with some keyword arguments. Its |
|
916 |
``__call__`` method accepts the view object (which will be a class). It uses |
8aa1c2
|
917 |
the ``attr`` keyword argument it is passed to determine which attribute should |
SP |
918 |
be used as an action method. The wrapper method it returns accepts ``(context, |
|
919 |
request)`` and returns the result of calling the action method with keyword |
|
920 |
arguments implied by the :term:`matchdict` after popping the ``action`` out of |
|
921 |
it. This somewhat emulates the Pylons style of calling action methods with |
|
922 |
routing parameters pulled out of the route matching dict as keyword arguments. |
81a833
|
923 |
|
CM |
924 |
.. code-block:: python |
|
925 |
:linenos: |
|
926 |
|
c5e83c
|
927 |
# framework |
81a833
|
928 |
|
c5e83c
|
929 |
class PylonsControllerViewMapper(object): |
CM |
930 |
def __init__(self, **kw): |
|
931 |
self.kw = kw |
81a833
|
932 |
|
c5e83c
|
933 |
def __call__(self, view): |
CM |
934 |
attr = self.kw['attr'] |
|
935 |
def wrapper(context, request): |
|
936 |
matchdict = request.matchdict.copy() |
|
937 |
matchdict.pop('action', None) |
46eccc
|
938 |
inst = view(request) |
c5e83c
|
939 |
meth = getattr(inst, attr) |
CM |
940 |
return meth(**matchdict) |
|
941 |
return wrapper |
81a833
|
942 |
|
c5e83c
|
943 |
class BaseController(object): |
CM |
944 |
__view_mapper__ = PylonsControllerViewMapper |
81a833
|
945 |
|
c5e83c
|
946 |
A user might make use of these framework components like so: |
81a833
|
947 |
|
CM |
948 |
.. code-block:: python |
|
949 |
:linenos: |
|
950 |
|
c5e83c
|
951 |
# user application |
CM |
952 |
|
d868ff
|
953 |
from pyramid.response import Response |
d7f259
|
954 |
from pyramid.config import Configurator |
2fa576
|
955 |
import pyramid_handlers |
cfb2b5
|
956 |
from wsgiref.simple_server import make_server |
81a833
|
957 |
|
c5e83c
|
958 |
class MyController(BaseController): |
CM |
959 |
def index(self, id): |
|
960 |
return Response(id) |
81a833
|
961 |
|
c5e83c
|
962 |
if __name__ == '__main__': |
CM |
963 |
config = Configurator() |
2fa576
|
964 |
config.include(pyramid_handlers) |
c5e83c
|
965 |
config.add_handler('one', '/{id}', MyController, action='index') |
CM |
966 |
config.add_handler('two', '/{action}/{id}', MyController) |
cfb2b5
|
967 |
server.make_server('0.0.0.0', 8080, config.make_wsgi_app()) |
CM |
968 |
server.serve_forever() |
81a833
|
969 |
|
cae85d
|
970 |
The :meth:`pyramid.config.Configurator.set_view_mapper` method can be used to |
CM |
971 |
set a *default* view mapper (overriding the superdefault view mapper used by |
|
972 |
Pyramid itself). |
250c02
|
973 |
|
8aa1c2
|
974 |
A *single* view registration can use a view mapper by passing the mapper as the |
SP |
975 |
``mapper`` argument to :meth:`~pyramid.config.Configurator.add_view`. |
250c02
|
976 |
|
c5e83c
|
977 |
.. index:: |
74abc0
|
978 |
single: configuration decorator |
81d3b5
|
979 |
|
6bc662
|
980 |
.. _registering_configuration_decorators: |
CM |
981 |
|
7200cb
|
982 |
Registering Configuration Decorators |
CM |
983 |
------------------------------------ |
7aa7fd
|
984 |
|
8aa1c2
|
985 |
Decorators such as :class:`~pyramid.view.view_config` don't change the behavior |
SP |
986 |
of the functions or classes they're decorating. Instead when a :term:`scan` is |
|
987 |
performed, a modified version of the function or class is registered with |
|
988 |
:app:`Pyramid`. |
7aa7fd
|
989 |
|
e1a7e0
|
990 |
You may wish to have your own decorators that offer such behaviour. This is |
8aa1c2
|
991 |
possible by using the :term:`Venusian` package in the same way that it is used |
SP |
992 |
by :app:`Pyramid`. |
7aa7fd
|
993 |
|
e1a7e0
|
994 |
By way of example, let's suppose you want to write a decorator that registers |
CM |
995 |
the function it wraps with a :term:`Zope Component Architecture` "utility" |
|
996 |
within the :term:`application registry` provided by :app:`Pyramid`. The |
|
997 |
application registry and the utility inside the registry is likely only to be |
|
998 |
available once your application's configuration is at least partially |
|
999 |
completed. A normal decorator would fail as it would be executed before the |
|
1000 |
configuration had even begun. |
7aa7fd
|
1001 |
|
8aa1c2
|
1002 |
However, using :term:`Venusian`, the decorator could be written as follows: |
7aa7fd
|
1003 |
|
CW |
1004 |
.. code-block:: python |
|
1005 |
:linenos: |
|
1006 |
|
|
1007 |
import venusian |
7200cb
|
1008 |
from mypackage.interfaces import IMyUtility |
879bb5
|
1009 |
|
7aa7fd
|
1010 |
class registerFunction(object): |
879bb5
|
1011 |
|
c70c74
|
1012 |
def __init__(self, path): |
7aa7fd
|
1013 |
self.path = path |
CW |
1014 |
|
c70c74
|
1015 |
def register(self, scanner, name, wrapped): |
780b6f
|
1016 |
registry = scanner.config.registry |
7200cb
|
1017 |
registry.getUtility(IMyUtility).register( |
879bb5
|
1018 |
self.path, wrapped) |
CZ |
1019 |
|
c70c74
|
1020 |
def __call__(self, wrapped): |
7200cb
|
1021 |
venusian.attach(wrapped, self.register) |
7aa7fd
|
1022 |
return wrapped |
879bb5
|
1023 |
|
8aa1c2
|
1024 |
This decorator could then be used to register functions throughout your code: |
7aa7fd
|
1025 |
|
CW |
1026 |
.. code-block:: python |
|
1027 |
:linenos: |
|
1028 |
|
|
1029 |
@registerFunction('/some/path') |
|
1030 |
def my_function(): |
8aa1c2
|
1031 |
do_stuff() |
7aa7fd
|
1032 |
|
8aa1c2
|
1033 |
However, the utility would only be looked up when a :term:`scan` was performed, |
SP |
1034 |
enabling you to set up the utility in advance: |
7aa7fd
|
1035 |
|
CW |
1036 |
.. code-block:: python |
|
1037 |
:linenos: |
|
1038 |
|
1ca5b3
|
1039 |
from zope.interface import implementer |
CM |
1040 |
|
10f1b5
|
1041 |
from wsgiref.simple_server import make_server |
d7f259
|
1042 |
from pyramid.config import Configurator |
879bb5
|
1043 |
from mypackage.interfaces import IMyUtility |
7aa7fd
|
1044 |
|
1ca5b3
|
1045 |
@implementer(IMyUtility) |
7aa7fd
|
1046 |
class UtilityImplementation: |
CW |
1047 |
|
|
1048 |
def __init__(self): |
8aa1c2
|
1049 |
self.registrations = {} |
7aa7fd
|
1050 |
|
879bb5
|
1051 |
def register(self, path, callable_): |
8aa1c2
|
1052 |
self.registrations[path] = callable_ |
7aa7fd
|
1053 |
|
CW |
1054 |
if __name__ == '__main__': |
|
1055 |
config = Configurator() |
|
1056 |
config.registry.registerUtility(UtilityImplementation()) |
|
1057 |
config.scan() |
|
1058 |
app = config.make_wsgi_app() |
10f1b5
|
1059 |
server = make_server('0.0.0.0', 8080, app) |
CM |
1060 |
server.serve_forever() |
7aa7fd
|
1061 |
|
93c9be
|
1062 |
For full details, please read the :ref:`Venusian documentation <venusian:venusian>`. |
SP |
1063 |
|
6bc662
|
1064 |
|
131132
|
1065 |
.. _registering_tweens: |
CM |
1066 |
|
04e6bf
|
1067 |
Registering Tweens |
AH |
1068 |
------------------ |
131132
|
1069 |
|
40dbf4
|
1070 |
.. versionadded:: 1.2 |
TL |
1071 |
Tweens |
131132
|
1072 |
|
05f610
|
1073 |
A :term:`tween` (a contraction of the word "between") is a bit of code that |
CM |
1074 |
sits between the Pyramid router's main request handling function and the |
|
1075 |
upstream WSGI component that uses :app:`Pyramid` as its "app". This is a |
8aa1c2
|
1076 |
feature that may be used by Pyramid framework extensions to provide, for |
05f610
|
1077 |
example, Pyramid-specific view timing support bookkeeping code that examines |
131132
|
1078 |
exceptions before they are returned to the upstream WSGI application. Tweens |
8aa1c2
|
1079 |
behave a bit like :term:`WSGI` :term:`middleware`, but they have the benefit of |
04e6bf
|
1080 |
running in a context in which they have access to the Pyramid :term:`request`, |
8aa1c2
|
1081 |
:term:`response`, and :term:`application registry`, as well as the Pyramid |
04e6bf
|
1082 |
rendering machinery. |
131132
|
1083 |
|
04e6bf
|
1084 |
Creating a Tween |
AH |
1085 |
~~~~~~~~~~~~~~~~ |
539646
|
1086 |
|
8aa1c2
|
1087 |
To create a tween, you must write a "tween factory". A tween factory must be a |
SP |
1088 |
globally importable callable which accepts two arguments: ``handler`` and |
|
1089 |
``registry``. ``handler`` will be either the main Pyramid request handling |
|
1090 |
function or another tween. ``registry`` will be the Pyramid :term:`application |
|
1091 |
registry` represented by this Configurator. A tween factory must return the |
|
1092 |
tween (a callable object) when it is called. |
131132
|
1093 |
|
04e6bf
|
1094 |
A tween is called with a single argument, ``request``, which is the |
8aa1c2
|
1095 |
:term:`request` created by Pyramid's router when it receives a WSGI request. A |
SP |
1096 |
tween should return a :term:`response`, usually the one generated by the |
04e6bf
|
1097 |
downstream Pyramid application. |
bf669a
|
1098 |
|
04e6bf
|
1099 |
You can write the tween factory as a simple closure-returning function: |
AH |
1100 |
|
|
1101 |
.. code-block:: python |
392a6c
|
1102 |
:linenos: |
04e6bf
|
1103 |
|
AH |
1104 |
def simple_tween_factory(handler, registry): |
|
1105 |
# one-time configuration code goes here |
|
1106 |
|
|
1107 |
def simple_tween(request): |
|
1108 |
# code to be executed for each request before |
|
1109 |
# the actual application code goes here |
|
1110 |
|
|
1111 |
response = handler(request) |
|
1112 |
|
|
1113 |
# code to be executed for each request after |
|
1114 |
# the actual application code goes here |
|
1115 |
|
|
1116 |
return response |
|
1117 |
|
fab44e
|
1118 |
return simple_tween |
04e6bf
|
1119 |
|
a0ef13
|
1120 |
Alternatively, the tween factory can be a class with the ``__call__`` magic |
bf669a
|
1121 |
method: |
04e6bf
|
1122 |
|
AH |
1123 |
.. code-block:: python |
392a6c
|
1124 |
:linenos: |
04e6bf
|
1125 |
|
AH |
1126 |
class simple_tween_factory(object): |
fbcce7
|
1127 |
def __init__(self, handler, registry): |
04e6bf
|
1128 |
self.handler = handler |
AH |
1129 |
self.registry = registry |
|
1130 |
|
|
1131 |
# one-time configuration code goes here |
|
1132 |
|
|
1133 |
def __call__(self, request): |
|
1134 |
# code to be executed for each request before |
|
1135 |
# the actual application code goes here |
|
1136 |
|
|
1137 |
response = self.handler(request) |
|
1138 |
|
|
1139 |
# code to be executed for each request after |
|
1140 |
# the actual application code goes here |
|
1141 |
|
|
1142 |
return response |
|
1143 |
|
8aa1c2
|
1144 |
You should avoid mutating any state on the tween instance. The tween is invoked |
SP |
1145 |
once per request and any shared mutable state needs to be carefully handled to |
|
1146 |
avoid any race conditions. |
2c096b
|
1147 |
|
04e6bf
|
1148 |
The closure style performs slightly better and enables you to conditionally |
AH |
1149 |
omit the tween from the request processing pipeline (see the following timing |
|
1150 |
tween example), whereas the class style makes it easier to have shared mutable |
8aa1c2
|
1151 |
state and allows subclassing. |
04e6bf
|
1152 |
|
AH |
1153 |
Here's a complete example of a tween that logs the time spent processing each |
|
1154 |
request: |
131132
|
1155 |
|
CM |
1156 |
.. code-block:: python |
392a6c
|
1157 |
:linenos: |
539646
|
1158 |
|
CM |
1159 |
# in a module named myapp.tweens |
131132
|
1160 |
|
CM |
1161 |
import time |
|
1162 |
from pyramid.settings import asbool |
|
1163 |
import logging |
|
1164 |
|
|
1165 |
log = logging.getLogger(__name__) |
|
1166 |
|
|
1167 |
def timing_tween_factory(handler, registry): |
|
1168 |
if asbool(registry.settings.get('do_timing')): |
|
1169 |
# if timing support is enabled, return a wrapper |
|
1170 |
def timing_tween(request): |
|
1171 |
start = time.time() |
|
1172 |
try: |
8d1533
|
1173 |
response = handler(request) |
131132
|
1174 |
finally: |
CM |
1175 |
end = time.time() |
|
1176 |
log.debug('The request took %s seconds' % |
|
1177 |
(end - start)) |
8d1533
|
1178 |
return response |
131132
|
1179 |
return timing_tween |
CM |
1180 |
# if timing support is not enabled, return the original |
|
1181 |
# handler |
|
1182 |
return handler |
539646
|
1183 |
|
CM |
1184 |
In the above example, the tween factory defines a ``timing_tween`` tween and |
|
1185 |
returns it if ``asbool(registry.settings.get('do_timing'))`` is true. It |
8aa1c2
|
1186 |
otherwise simply returns the handler which it was given. The |
SP |
1187 |
``registry.settings`` attribute is a handle to the deployment settings provided |
|
1188 |
by the user (usually in an ``.ini`` file). In this case, if the user has |
|
1189 |
defined a ``do_timing`` setting and that setting is ``True``, the user has said |
|
1190 |
they want to do timing, so the tween factory returns the timing tween; it |
|
1191 |
otherwise just returns the handler it has been provided, preventing any timing. |
539646
|
1192 |
|
CM |
1193 |
The example timing tween simply records the start time, calls the downstream |
|
1194 |
handler, logs the number of seconds consumed by the downstream handler, and |
|
1195 |
returns the response. |
|
1196 |
|
|
1197 |
Registering an Implicit Tween Factory |
|
1198 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
1199 |
|
|
1200 |
Once you've created a tween factory, you can register it into the implicit |
|
1201 |
tween chain using the :meth:`pyramid.config.Configurator.add_tween` method |
|
1202 |
using its :term:`dotted Python name`. |
|
1203 |
|
991a1a
|
1204 |
Here's an example of registering a tween factory as an "implicit" tween in a |
CI |
1205 |
Pyramid application: |
539646
|
1206 |
|
CM |
1207 |
.. code-block:: python |
392a6c
|
1208 |
:linenos: |
539646
|
1209 |
|
131132
|
1210 |
from pyramid.config import Configurator |
CM |
1211 |
config = Configurator() |
539646
|
1212 |
config.add_tween('myapp.tweens.timing_tween_factory') |
131132
|
1213 |
|
539646
|
1214 |
Note that you must use a :term:`dotted Python name` as the first argument to |
CM |
1215 |
:meth:`pyramid.config.Configurator.add_tween`; this must point at a tween |
|
1216 |
factory. You cannot pass the tween factory object itself to the method: it |
8aa1c2
|
1217 |
must be :term:`dotted Python name` that points to a globally importable object. |
SP |
1218 |
In the above example, we assume that a ``timing_tween_factory`` tween factory |
|
1219 |
was defined in a module named ``myapp.tweens``, so the tween factory is |
|
1220 |
importable as ``myapp.tweens.timing_tween_factory``. |
539646
|
1221 |
|
8aa1c2
|
1222 |
When you use :meth:`pyramid.config.Configurator.add_tween`, you're instructing |
SP |
1223 |
the system to use your tween factory at startup time unless the user has |
|
1224 |
provided an explicit tween list in their configuration. This is what's meant |
|
1225 |
by an "implicit" tween. A user can always elect to supply an explicit tween |
|
1226 |
list, reordering or disincluding implicitly added tweens. See |
539646
|
1227 |
:ref:`explicit_tween_ordering` for more information about explicit tween |
CM |
1228 |
ordering. |
131132
|
1229 |
|
8aa1c2
|
1230 |
If more than one call to :meth:`pyramid.config.Configurator.add_tween` is made |
SP |
1231 |
within a single application configuration, the tweens will be chained together |
|
1232 |
at application startup time. The *first* tween factory added via ``add_tween`` |
|
1233 |
will be called with the Pyramid exception view tween factory as its ``handler`` |
|
1234 |
argument, then the tween factory added directly after that one will be called |
|
1235 |
with the result of the first tween factory as its ``handler`` argument, and so |
|
1236 |
on, ad infinitum until all tween factories have been called. The Pyramid router |
|
1237 |
will use the outermost tween produced by this chain (the tween generated by the |
|
1238 |
very last tween factory added) as its request handler function. For example: |
131132
|
1239 |
|
05f610
|
1240 |
.. code-block:: python |
392a6c
|
1241 |
:linenos: |
131132
|
1242 |
|
05f610
|
1243 |
from pyramid.config import Configurator |
CM |
1244 |
|
|
1245 |
config = Configurator() |
|
1246 |
config.add_tween('myapp.tween_factory1') |
|
1247 |
config.add_tween('myapp.tween_factory2') |
|
1248 |
|
8aa1c2
|
1249 |
The above example will generate an implicit tween chain that looks like this:: |
05f610
|
1250 |
|
CM |
1251 |
INGRESS (implicit) |
|
1252 |
myapp.tween_factory2 |
|
1253 |
myapp.tween_factory1 |
|
1254 |
pyramid.tweens.excview_tween_factory (implicit) |
|
1255 |
MAIN (implicit) |
|
1256 |
|
539646
|
1257 |
Suggesting Implicit Tween Ordering |
CM |
1258 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
1259 |
|
05f610
|
1260 |
By default, as described above, the ordering of the chain is controlled |
CM |
1261 |
entirely by the relative ordering of calls to |
|
1262 |
:meth:`pyramid.config.Configurator.add_tween`. However, the caller of |
8aa1c2
|
1263 |
``add_tween`` can provide an optional hint that can influence the implicit |
SP |
1264 |
tween chain ordering by supplying ``under`` or ``over`` (or both) arguments to |
|
1265 |
:meth:`~pyramid.config.Configurator.add_tween`. These hints are only used when |
|
1266 |
an explicit tween ordering is not used. See :ref:`explicit_tween_ordering` for |
|
1267 |
a description of how to set an explicit tween ordering. |
05f610
|
1268 |
|
CM |
1269 |
Allowable values for ``under`` or ``over`` (or both) are: |
|
1270 |
|
8aa1c2
|
1271 |
- ``None`` (the default), |
05f610
|
1272 |
|
8aa1c2
|
1273 |
- a :term:`dotted Python name` to a tween factory: a string representing the |
SP |
1274 |
predicted dotted name of a tween factory added in a call to ``add_tween`` in |
|
1275 |
the same configuration session, |
05f610
|
1276 |
|
8aa1c2
|
1277 |
- one of the constants :attr:`pyramid.tweens.MAIN`, |
SP |
1278 |
:attr:`pyramid.tweens.INGRESS`, or :attr:`pyramid.tweens.EXCVIEW`, or |
05f610
|
1279 |
|
8aa1c2
|
1280 |
- an iterable of any combination of the above. This allows the user to specify |
9ec766
|
1281 |
fallbacks if the desired tween is not included, as well as compatibility |
MM |
1282 |
with multiple other tweens. |
|
1283 |
|
8aa1c2
|
1284 |
Effectively, ``over`` means "closer to the request ingress than" and ``under`` |
SP |
1285 |
means "closer to the main Pyramid application than". You can think of an onion |
|
1286 |
with outer layers over the inner layers, the application being under all the |
|
1287 |
layers at the center. |
05f610
|
1288 |
|
CM |
1289 |
For example, the following call to |
8aa1c2
|
1290 |
:meth:`~pyramid.config.Configurator.add_tween` will attempt to place the tween |
SP |
1291 |
factory represented by ``myapp.tween_factory`` directly "above" (in ``ptweens`` |
|
1292 |
order) the main Pyramid request handler. |
05f610
|
1293 |
|
CM |
1294 |
.. code-block:: python |
|
1295 |
:linenos: |
|
1296 |
|
|
1297 |
import pyramid.tweens |
|
1298 |
|
|
1299 |
config.add_tween('myapp.tween_factory', over=pyramid.tweens.MAIN) |
|
1300 |
|
8aa1c2
|
1301 |
The above example will generate an implicit tween chain that looks like this:: |
05f610
|
1302 |
|
CM |
1303 |
INGRESS (implicit) |
|
1304 |
pyramid.tweens.excview_tween_factory (implicit) |
|
1305 |
myapp.tween_factory |
|
1306 |
MAIN (implicit) |
|
1307 |
|
|
1308 |
Likewise, calling the following call to |
8aa1c2
|
1309 |
:meth:`~pyramid.config.Configurator.add_tween` will attempt to place this tween |
SP |
1310 |
factory "above" the main handler but "below" a separately added tween factory: |
05f610
|
1311 |
|
CM |
1312 |
.. code-block:: python |
|
1313 |
:linenos: |
|
1314 |
|
|
1315 |
import pyramid.tweens |
|
1316 |
|
|
1317 |
config.add_tween('myapp.tween_factory1', |
|
1318 |
over=pyramid.tweens.MAIN) |
|
1319 |
config.add_tween('myapp.tween_factory2', |
|
1320 |
over=pyramid.tweens.MAIN, |
|
1321 |
under='myapp.tween_factory1') |
|
1322 |
|
8aa1c2
|
1323 |
The above example will generate an implicit tween chain that looks like this:: |
05f610
|
1324 |
|
CM |
1325 |
INGRESS (implicit) |
|
1326 |
pyramid.tweens.excview_tween_factory (implicit) |
|
1327 |
myapp.tween_factory1 |
|
1328 |
myapp.tween_factory2 |
|
1329 |
MAIN (implicit) |
|
1330 |
|
|
1331 |
Specifying neither ``over`` nor ``under`` is equivalent to specifying |
|
1332 |
``under=INGRESS``. |
|
1333 |
|
9ec766
|
1334 |
If all options for ``under`` (or ``over``) cannot be found in the current |
MM |
1335 |
configuration, it is an error. If some options are specified purely for |
8aa1c2
|
1336 |
compatibilty with other tweens, just add a fallback of ``MAIN`` or ``INGRESS``. |
SP |
1337 |
For example, ``under=('someothertween', 'someothertween2', INGRESS)``. This |
|
1338 |
constraint will require the tween to be located under the ``someothertween`` |
|
1339 |
tween, the ``someothertween2`` tween, and ``INGRESS``. If any of these is not |
|
1340 |
in the current configuration, this constraint will only organize itself based |
|
1341 |
on the tweens that are present. |
05f610
|
1342 |
|
be46da
|
1343 |
.. _explicit_tween_ordering: |
CM |
1344 |
|
|
1345 |
Explicit Tween Ordering |
|
1346 |
~~~~~~~~~~~~~~~~~~~~~~~ |
|
1347 |
|
8aa1c2
|
1348 |
Implicit tween ordering is obviously only best-effort. Pyramid will attempt to |
SP |
1349 |
provide an implicit order of tweens as best it can using hints provided by |
|
1350 |
calls to :meth:`~pyramid.config.Configurator.add_tween`. But because it's only |
|
1351 |
best-effort, if very precise tween ordering is required, the only surefire way |
|
1352 |
to get it is to use an explicit tween order. The deploying user can override |
|
1353 |
the implicit tween inclusion and ordering implied by calls to |
05f610
|
1354 |
:meth:`~pyramid.config.Configurator.add_tween` entirely by using the |
CM |
1355 |
``pyramid.tweens`` settings value. When used, this settings value must be a |
8aa1c2
|
1356 |
list of Python dotted names which will override the ordering (and inclusion) of |
SP |
1357 |
tween factories in the implicit tween chain. For example: |
131132
|
1358 |
|
CM |
1359 |
.. code-block:: ini |
|
1360 |
:linenos: |
|
1361 |
|
|
1362 |
[app:main] |
|
1363 |
use = egg:MyApp |
|
1364 |
pyramid.reload_templates = true |
|
1365 |
pyramid.debug_authorization = false |
|
1366 |
pyramid.debug_notfound = false |
|
1367 |
pyramid.debug_routematch = false |
|
1368 |
pyramid.debug_templates = true |
05f610
|
1369 |
pyramid.tweens = myapp.my_cool_tween_factory |
CM |
1370 |
pyramid.tweens.excview_tween_factory |
131132
|
1371 |
|
CM |
1372 |
In the above configuration, calls made during configuration to |
|
1373 |
:meth:`pyramid.config.Configurator.add_tween` are ignored, and the user is |
|
1374 |
telling the system to use the tween factories he has listed in the |
8aa1c2
|
1375 |
``pyramid.tweens`` configuration setting (each is a :term:`dotted Python name` |
SP |
1376 |
which points to a tween factory) instead of any tween factories added via |
|
1377 |
:meth:`pyramid.config.Configurator.add_tween`. The *first* tween factory in |
|
1378 |
the ``pyramid.tweens`` list will be used as the producer of the effective |
131132
|
1379 |
:app:`Pyramid` request handling function; it will wrap the tween factory |
8aa1c2
|
1380 |
declared directly "below" it, ad infinitum. The "main" Pyramid request handler |
SP |
1381 |
is implicit, and always "at the bottom". |
131132
|
1382 |
|
012b97
|
1383 |
.. note:: |
M |
1384 |
|
8aa1c2
|
1385 |
Pyramid's own :term:`exception view` handling logic is implemented as a |
SP |
1386 |
tween factory function: :func:`pyramid.tweens.excview_tween_factory`. If |
|
1387 |
Pyramid exception view handling is desired, and tween factories are |
131132
|
1388 |
specified via the ``pyramid.tweens`` configuration setting, the |
CM |
1389 |
:func:`pyramid.tweens.excview_tween_factory` function must be added to the |
|
1390 |
``pyramid.tweens`` configuration setting list explicitly. If it is not |
|
1391 |
present, Pyramid will not perform exception view handling. |
|
1392 |
|
be46da
|
1393 |
Tween Conflicts and Ordering Cycles |
CM |
1394 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
1395 |
|
8aa1c2
|
1396 |
Pyramid will prevent the same tween factory from being added to the tween chain |
SP |
1397 |
more than once using configuration conflict detection. If you wish to add the |
|
1398 |
same tween factory more than once in a configuration, you should either: (a) |
|
1399 |
use a tween factory that is a separate globally importable instance object from |
|
1400 |
the factory that it conflicts with; (b) use a function or class as a tween |
|
1401 |
factory with the same logic as the other tween factory it conflicts with, but |
|
1402 |
with a different ``__name__`` attribute; or (c) call |
157c29
|
1403 |
:meth:`pyramid.config.Configurator.commit` between calls to |
CM |
1404 |
:meth:`pyramid.config.Configurator.add_tween`. |
131132
|
1405 |
|
05f610
|
1406 |
If a cycle is detected in implicit tween ordering when ``over`` and ``under`` |
8aa1c2
|
1407 |
are used in any call to ``add_tween``, an exception will be raised at startup |
05f610
|
1408 |
time. |
CM |
1409 |
|
be46da
|
1410 |
Displaying Tween Ordering |
CM |
1411 |
~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
1412 |
|
8aa1c2
|
1413 |
The ``ptweens`` command-line utility can be used to report the current implict |
SP |
1414 |
and explicit tween chains used by an application. See |
05f610
|
1415 |
:ref:`displaying_tweens`. |
0196b2
|
1416 |
|
CM |
1417 |
.. _registering_thirdparty_predicates: |
|
1418 |
|
8aa1c2
|
1419 |
Adding a Third Party View, Route, or Subscriber Predicate |
95f766
|
1420 |
--------------------------------------------------------- |
0196b2
|
1421 |
|
40dbf4
|
1422 |
.. versionadded:: 1.4 |
5664c4
|
1423 |
|
95f766
|
1424 |
.. _view_and_route_predicates: |
CM |
1425 |
|
|
1426 |
View and Route Predicates |
|
1427 |
~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
1428 |
|
8aa1c2
|
1429 |
View and route predicates used during configuration allow you to narrow the set |
SP |
1430 |
of circumstances under which a view or route will match. For example, the |
|
1431 |
``request_method`` view predicate can be used to ensure a view callable is only |
|
1432 |
invoked when the request's method is ``POST``: |
0196b2
|
1433 |
|
CM |
1434 |
.. code-block:: python |
|
1435 |
|
|
1436 |
@view_config(request_method='POST') |
|
1437 |
def someview(request): |
|
1438 |
... |
|
1439 |
|
|
1440 |
Likewise, a similar predicate can be used as a *route* predicate: |
|
1441 |
|
|
1442 |
.. code-block:: python |
|
1443 |
|
|
1444 |
config.add_route('name', '/foo', request_method='POST') |
|
1445 |
|
8aa1c2
|
1446 |
Many other built-in predicates exists (``request_param``, and others). You can |
SP |
1447 |
add third-party predicates to the list of available predicates by using one of |
|
1448 |
:meth:`pyramid.config.Configurator.add_view_predicate` or |
0196b2
|
1449 |
:meth:`pyramid.config.Configurator.add_route_predicate`. The former adds a |
CM |
1450 |
view predicate, the latter a route predicate. |
|
1451 |
|
|
1452 |
When using one of those APIs, you pass a *name* and a *factory* to add a |
|
1453 |
predicate during Pyramid's configuration stage. For example: |
|
1454 |
|
|
1455 |
.. code-block:: python |
|
1456 |
|
|
1457 |
config.add_view_predicate('content_type', ContentTypePredicate) |
|
1458 |
|
|
1459 |
The above example adds a new predicate named ``content_type`` to the list of |
|
1460 |
available predicates for views. This will allow the following view |
|
1461 |
configuration statement to work: |
|
1462 |
|
|
1463 |
.. code-block:: python |
|
1464 |
:linenos: |
|
1465 |
|
|
1466 |
@view_config(content_type='File') |
|
1467 |
def aview(request): ... |
|
1468 |
|
|
1469 |
The first argument to :meth:`pyramid.config.Configurator.add_view_predicate`, |
|
1470 |
the name, is a string representing the name that is expected to be passed to |
|
1471 |
``view_config`` (or its imperative analogue ``add_view``). |
|
1472 |
|
d71aca
|
1473 |
The second argument is a view or route predicate factory, or a :term:`dotted |
BJR |
1474 |
Python name` which refers to a view or route predicate factory. A view or |
|
1475 |
route predicate factory is most often a class with a constructor |
8aa1c2
|
1476 |
(``__init__``), a ``text`` method, a ``phash`` method, and a ``__call__`` |
d71aca
|
1477 |
method. For example: |
0196b2
|
1478 |
|
CM |
1479 |
.. code-block:: python |
392a6c
|
1480 |
:linenos: |
0196b2
|
1481 |
|
CM |
1482 |
class ContentTypePredicate(object): |
|
1483 |
def __init__(self, val, config): |
cff71c
|
1484 |
self.val = val |
0196b2
|
1485 |
|
CM |
1486 |
def text(self): |
|
1487 |
return 'content_type = %s' % (self.val,) |
|
1488 |
|
|
1489 |
phash = text |
|
1490 |
|
|
1491 |
def __call__(self, context, request): |
ad05d1
|
1492 |
return request.content_type == self.val |
0196b2
|
1493 |
|
CM |
1494 |
The constructor of a predicate factory takes two arguments: ``val`` and |
|
1495 |
``config``. The ``val`` argument will be the argument passed to |
8aa1c2
|
1496 |
``view_config`` (or ``add_view``). In the example above, it will be the string |
SP |
1497 |
``File``. The second argument, ``config``, will be the Configurator instance |
|
1498 |
at the time of configuration. |
0196b2
|
1499 |
|
8aa1c2
|
1500 |
The ``text`` method must return a string. It should be useful to describe the |
SP |
1501 |
behavior of the predicate in error messages. |
0196b2
|
1502 |
|
8aa1c2
|
1503 |
The ``phash`` method must return a string or a sequence of strings. It's most |
SP |
1504 |
often the same as ``text``, as long as ``text`` uniquely describes the |
|
1505 |
predicate's name and the value passed to the constructor. If ``text`` is more |
|
1506 |
general, or doesn't describe things that way, ``phash`` should return a string |
|
1507 |
with the name and the value serialized. The result of ``phash`` is not seen in |
|
1508 |
output anywhere, it just informs the uniqueness constraints for view |
|
1509 |
configuration. |
0196b2
|
1510 |
|
ad05d1
|
1511 |
The ``__call__`` method differs depending on whether the predicate is used as |
MM |
1512 |
a :term:`view predicate` or a :term:`route predicate`: |
0196b2
|
1513 |
|
ad05d1
|
1514 |
- When used as a route predicate, the ``__call__`` signature is |
MM |
1515 |
``(info, request)``. The ``info`` object is a dictionary containing two |
|
1516 |
keys: ``match`` and ``route``. ``info['match']`` is the matchdict containing |
|
1517 |
the patterns matched in the route pattern. ``info['route']`` is the |
|
1518 |
:class:`pyramid.interfaces.IRoute` object for the current route. |
|
1519 |
|
|
1520 |
- When used as a view predicate, the ``__call__`` signature is |
|
1521 |
``(context, request)``. The ``context`` is the result of :term:`traversal` |
|
1522 |
performed using either the route's :term:`root factory` or the app's |
|
1523 |
:term:`default root factory`. |
|
1524 |
|
|
1525 |
In both cases the ``__call__`` method is expected to return ``True`` or |
|
1526 |
``False``. |
|
1527 |
|
|
1528 |
It is possible to use the same predicate factory as both a view predicate and |
|
1529 |
as a route predicate, but they'll need to handle the ``info`` or ``context`` |
|
1530 |
argument specially (many predicates do not need this argument) and you'll need |
|
1531 |
to call ``add_view_predicate`` and ``add_route_predicate`` separately with |
|
1532 |
the same factory. |
0196b2
|
1533 |
|
95f766
|
1534 |
.. _subscriber_predicates: |
CM |
1535 |
|
|
1536 |
Subscriber Predicates |
|
1537 |
~~~~~~~~~~~~~~~~~~~~~ |
|
1538 |
|
8aa1c2
|
1539 |
Subscriber predicates work almost exactly like view and route predicates. They |
SP |
1540 |
narrow the set of circumstances in which a subscriber will be called. There are |
|
1541 |
several minor differences between a subscriber predicate and a view or route |
|
1542 |
predicate: |
95f766
|
1543 |
|
CM |
1544 |
- There are no default subscriber predicates. You must register one to use |
|
1545 |
one. |
|
1546 |
|
8aa1c2
|
1547 |
- The ``__call__`` method of a subscriber predicate accepts a single ``event`` |
SP |
1548 |
object instead of a ``context`` and a ``request``. |
95f766
|
1549 |
|
CM |
1550 |
- Not every subscriber predicate can be used with every event type. Some |
|
1551 |
subscriber predicates will assume a certain event type. |
|
1552 |
|
|
1553 |
Here's an example of a subscriber predicate that can be used in conjunction |
46d505
|
1554 |
with a subscriber that subscribes to the :class:`pyramid.events.NewRequest` |
95f766
|
1555 |
event type. |
CM |
1556 |
|
|
1557 |
.. code-block:: python |
392a6c
|
1558 |
:linenos: |
95f766
|
1559 |
|
CM |
1560 |
class RequestPathStartsWith(object): |
|
1561 |
def __init__(self, val, config): |
|
1562 |
self.val = val |
|
1563 |
|
|
1564 |
def text(self): |
|
1565 |
return 'path_startswith = %s' % (self.val,) |
|
1566 |
|
|
1567 |
phash = text |
|
1568 |
|
|
1569 |
def __call__(self, event): |
|
1570 |
return event.request.path.startswith(self.val) |
|
1571 |
|
0b5e98
|
1572 |
Once you've created a subscriber predicate, it may be registered via |
95f766
|
1573 |
:meth:`pyramid.config.Configurator.add_subscriber_predicate`. For example: |
CM |
1574 |
|
|
1575 |
.. code-block:: python |
|
1576 |
|
|
1577 |
config.add_subscriber_predicate( |
|
1578 |
'request_path_startswith', RequestPathStartsWith) |
|
1579 |
|
|
1580 |
Once a subscriber predicate is registered, you can use it in a call to |
|
1581 |
:meth:`pyramid.config.Configurator.add_subscriber` or to |
8aa1c2
|
1582 |
:class:`pyramid.events.subscriber`. Here's an example of using the previously |
SP |
1583 |
registered ``request_path_startswith`` predicate in a call to |
95f766
|
1584 |
:meth:`~pyramid.config.Configurator.add_subscriber`: |
CM |
1585 |
|
|
1586 |
.. code-block:: python |
392a6c
|
1587 |
:linenos: |
95f766
|
1588 |
|
CM |
1589 |
# define a subscriber in your code |
|
1590 |
|
|
1591 |
def yosubscriber(event): |
|
1592 |
event.request.yo = 'YO!' |
|
1593 |
|
|
1594 |
# and at configuration time |
|
1595 |
|
8aa1c2
|
1596 |
config.add_subscriber(yosubscriber, NewRequest, |
95f766
|
1597 |
request_path_startswith='/add_yo') |
CM |
1598 |
|
|
1599 |
Here's the same subscriber/predicate/event-type combination used via |
|
1600 |
:class:`~pyramid.events.subscriber`. |
|
1601 |
|
|
1602 |
.. code-block:: python |
392a6c
|
1603 |
:linenos: |
95f766
|
1604 |
|
CM |
1605 |
from pyramid.events import subscriber |
|
1606 |
|
|
1607 |
@subscriber(NewRequest, request_path_startswith='/add_yo') |
|
1608 |
def yosubscriber(event): |
|
1609 |
event.request.yo = 'YO!' |
|
1610 |
|
8aa1c2
|
1611 |
In either of the above configurations, the ``yosubscriber`` callable will only |
SP |
1612 |
be called if the request path starts with ``/add_yo``. Otherwise the event |
|
1613 |
subscriber will not be called. |
95f766
|
1614 |
|
CM |
1615 |
Note that the ``request_path_startswith`` subscriber you defined can be used |
|
1616 |
with events that have a ``request`` attribute, but not ones that do not. So, |
|
1617 |
for example, the predicate can be used with subscribers registered for |
|
1618 |
:class:`pyramid.events.NewRequest` and :class:`pyramid.events.ContextFound` |
|
1619 |
events, but it cannot be used with subscribers registered for |
8aa1c2
|
1620 |
:class:`pyramid.events.ApplicationCreated` because the latter type of event has |
SP |
1621 |
no ``request`` attribute. The point being, unlike route and view predicates, |
|
1622 |
not every type of subscriber predicate will necessarily be applicable for use |
|
1623 |
in every subscriber registration. It is not the responsibility of the |
|
1624 |
predicate author to make every predicate make sense for every event type; it is |
|
1625 |
the responsibility of the predicate consumer to use predicates that make sense |
|
1626 |
for a particular event type registration. |
fdd1f8
|
1627 |
|
6fb44f
|
1628 |
|
fdd1f8
|
1629 |
.. index:: |
MM |
1630 |
single: view derivers |
|
1631 |
|
|
1632 |
.. _view_derivers: |
|
1633 |
|
|
1634 |
View Derivers |
|
1635 |
------------- |
|
1636 |
|
a11694
|
1637 |
.. versionadded:: 1.7 |
MM |
1638 |
|
fdd1f8
|
1639 |
Every URL processed by :app:`Pyramid` is matched against a custom view |
MM |
1640 |
pipeline. See :ref:`router_chapter` for how this works. The view pipeline |
6fb44f
|
1641 |
itself is built from the user-supplied :term:`view callable`, which is then |
fdd1f8
|
1642 |
composed with :term:`view derivers <view deriver>`. A view deriver is a |
MM |
1643 |
composable element of the view pipeline which is used to wrap a view with |
|
1644 |
added functionality. View derivers are very similar to the ``decorator`` |
6fb44f
|
1645 |
argument to :meth:`pyramid.config.Configurator.add_view`, except that they have |
fdd1f8
|
1646 |
the option to execute for every view in the application. |
MM |
1647 |
|
a11694
|
1648 |
It is helpful to think of a :term:`view deriver` as middleware for views. |
MM |
1649 |
Unlike tweens or WSGI middleware which are scoped to the application itself, |
6fb44f
|
1650 |
a view deriver is invoked once per view in the application, and can use |
a11694
|
1651 |
configuration options from the view to customize its behavior. |
MM |
1652 |
|
fdd1f8
|
1653 |
Built-in View Derivers |
MM |
1654 |
~~~~~~~~~~~~~~~~~~~~~~ |
|
1655 |
|
6fb44f
|
1656 |
There are several built-in view derivers that :app:`Pyramid` will automatically |
a11694
|
1657 |
apply to any view. Below they are defined in order from furthest to closest to |
fdd1f8
|
1658 |
the user-defined :term:`view callable`: |
a11694
|
1659 |
|
MM |
1660 |
``secured_view`` |
|
1661 |
|
6fb44f
|
1662 |
Enforce the ``permission`` defined on the view. This element is a no-op if no |
SP |
1663 |
permission is defined. Note there will always be a permission defined if a |
|
1664 |
default permission was assigned via |
e8c66a
|
1665 |
:meth:`pyramid.config.Configurator.set_default_permission` unless the |
MM |
1666 |
view is an :term:`exception view`. |
a11694
|
1667 |
|
a3db3c
|
1668 |
This element will also output useful debugging information when |
MM |
1669 |
``pyramid.debug_authorization`` is enabled. |
6b35eb
|
1670 |
|
MM |
1671 |
``csrf_view`` |
|
1672 |
|
|
1673 |
Used to check the CSRF token provided in the request. This element is a |
f2bc0e
|
1674 |
no-op if ``require_csrf`` view option is not ``True``. Note there will |
MM |
1675 |
always be a ``require_csrf`` option if a default value was assigned via |
e8c66a
|
1676 |
:meth:`pyramid.config.Configurator.set_default_csrf_options` unless |
MM |
1677 |
the view is an :term:`exception view`. |
a3db3c
|
1678 |
|
a11694
|
1679 |
``owrapped_view`` |
MM |
1680 |
|
|
1681 |
Invokes the wrapped view defined by the ``wrapper`` option. |
|
1682 |
|
|
1683 |
``http_cached_view`` |
|
1684 |
|
|
1685 |
Applies cache control headers to the response defined by the ``http_cache`` |
6fb44f
|
1686 |
option. This element is a no-op if the ``pyramid.prevent_http_cache`` setting |
a11694
|
1687 |
is enabled or the ``http_cache`` option is ``None``. |
MM |
1688 |
|
|
1689 |
``decorated_view`` |
|
1690 |
|
|
1691 |
Wraps the view with the decorators from the ``decorator`` option. |
|
1692 |
|
|
1693 |
``rendered_view`` |
|
1694 |
|
|
1695 |
Adapts the result of the :term:`view callable` into a :term:`response` |
|
1696 |
object. Below this point the result may be any Python object. |
fdd1f8
|
1697 |
|
MM |
1698 |
``mapped_view`` |
|
1699 |
|
|
1700 |
Applies the :term:`view mapper` defined by the ``mapper`` option or the |
|
1701 |
application's default view mapper to the :term:`view callable`. This |
|
1702 |
is always the closest deriver to the user-defined view and standardizes the |
|
1703 |
view pipeline interface to accept ``(context, request)`` from all previous |
|
1704 |
view derivers. |
|
1705 |
|
c231d8
|
1706 |
.. warning:: |
MM |
1707 |
|
|
1708 |
Any view derivers defined ``under`` the ``rendered_view`` are not |
|
1709 |
guaranteed to receive a valid response object. Rather they will receive the |
|
1710 |
result from the :term:`view mapper` which is likely the original response |
|
1711 |
returned from the view. This is possibly a dictionary for a renderer but it |
|
1712 |
may be any Python object that may be adapted into a response. |
|
1713 |
|
fdd1f8
|
1714 |
Custom View Derivers |
MM |
1715 |
~~~~~~~~~~~~~~~~~~~~ |
|
1716 |
|
6fb44f
|
1717 |
It is possible to define custom view derivers which will affect all views in an |
SP |
1718 |
application. There are many uses for this, but most will likely be centered |
|
1719 |
around monitoring and security. In order to register a custom :term:`view |
|
1720 |
deriver`, you should create a callable that conforms to the |
|
1721 |
:class:`pyramid.interfaces.IViewDeriver` interface, and then register it with |
35e632
|
1722 |
your application using :meth:`pyramid.config.Configurator.add_view_deriver`. |
e8c66a
|
1723 |
The callable should accept the ``view`` to be wrapped and the ``info`` object |
MM |
1724 |
which is an instance of :class:`pyramid.interfaces.IViewDeriverInfo`. |
35e632
|
1725 |
For example, below is a callable that can provide timing information for the |
MM |
1726 |
view pipeline: |
fdd1f8
|
1727 |
|
MM |
1728 |
.. code-block:: python |
|
1729 |
:linenos: |
|
1730 |
|
|
1731 |
import time |
|
1732 |
|
|
1733 |
def timing_view(view, info): |
16f989
|
1734 |
if info.options.get('timed'): |
CM |
1735 |
def wrapper_view(context, request): |
|
1736 |
start = time.time() |
|
1737 |
response = view(context, request) |
|
1738 |
end = time.time() |
|
1739 |
response.headers['X-View-Performance'] = '%.3f' % (end - start,) |
|
1740 |
return response |
|
1741 |
return wrapper_view |
|
1742 |
return view |
|
1743 |
|
|
1744 |
timing_view.options = ('timed',) |
fdd1f8
|
1745 |
|
c231d8
|
1746 |
config.add_view_deriver(timing_view) |
fdd1f8
|
1747 |
|
16f989
|
1748 |
The setting of ``timed`` on the timing_view signifies to Pyramid that ``timed`` |
CM |
1749 |
is a valid ``view_config`` keyword argument now. The ``timing_view`` custom |
|
1750 |
view deriver as registered above will only be active for any view defined with |
|
1751 |
a ``timed=True`` value passed as one of its ``view_config`` keywords. |
|
1752 |
|
|
1753 |
For example, this view configuration will *not* be a timed view: |
|
1754 |
|
|
1755 |
.. code-block:: python |
|
1756 |
:linenos: |
|
1757 |
|
|
1758 |
@view_config(route_name='home') |
|
1759 |
def home(request): |
|
1760 |
return Response('Home') |
|
1761 |
|
|
1762 |
But this view *will* have timing information added to the response headers: |
|
1763 |
|
|
1764 |
.. code-block:: python |
|
1765 |
:linenos: |
|
1766 |
|
|
1767 |
@view_config(route_name='home', timed=True) |
|
1768 |
def home(request): |
|
1769 |
return Response('Home') |
|
1770 |
|
fdd1f8
|
1771 |
View derivers are unique in that they have access to most of the options |
MM |
1772 |
passed to :meth:`pyramid.config.Configurator.add_view` in order to decide what |
6fb44f
|
1773 |
to do, and they have a chance to affect every view in the application. |
fdd1f8
|
1774 |
|
e8c66a
|
1775 |
.. _exception_view_derivers: |
MM |
1776 |
|
|
1777 |
Exception Views and View Derivers |
|
1778 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
1779 |
|
|
1780 |
A :term:`view deriver` has the opportunity to wrap any view, including |
|
1781 |
an :term:`exception view`. In general this is fine, but certain view derivers |
|
1782 |
may wish to avoid doing certain things when handling exceptions. For example, |
|
1783 |
the ``csrf_view`` and ``secured_view`` built-in view derivers will not perform |
|
1784 |
security checks on exception views unless explicitly told to do so. |
|
1785 |
|
|
1786 |
You can check for ``info.exception_only`` on the |
|
1787 |
:class:`pyramid.interfaces.IViewDeriverInfo` object when wrapping the view |
|
1788 |
to determine whether you are wrapping an exception view or a normal view. |
|
1789 |
|
fdd1f8
|
1790 |
Ordering View Derivers |
MM |
1791 |
~~~~~~~~~~~~~~~~~~~~~~ |
|
1792 |
|
6fb44f
|
1793 |
By default, every new view deriver is added between the ``decorated_view`` and |
SP |
1794 |
``rendered_view`` built-in derivers. It is possible to customize this ordering |
|
1795 |
using the ``over`` and ``under`` options. Each option can use the names of |
|
1796 |
other view derivers in order to specify an ordering. There should rarely be a |
c231d8
|
1797 |
reason to worry about the ordering of the derivers except when the deriver |
MM |
1798 |
depends on other operations in the view pipeline. |
6fb44f
|
1799 |
|
SP |
1800 |
Both ``over`` and ``under`` may also be iterables of constraints. For either |
|
1801 |
option, if one or more constraints was defined, at least one must be satisfied, |
|
1802 |
else a :class:`pyramid.exceptions.ConfigurationError` will be raised. This may |
|
1803 |
be used to define fallback constraints if another deriver is missing. |
c231d8
|
1804 |
|
MM |
1805 |
Two sentinel values exist, :attr:`pyramid.viewderivers.INGRESS` and |
|
1806 |
:attr:`pyramid.viewderivers.VIEW`, which may be used when specifying |
|
1807 |
constraints at the edges of the view pipeline. For example, to add a deriver |
|
1808 |
at the start of the pipeline you may use ``under=INGRESS``. |
fdd1f8
|
1809 |
|
a11694
|
1810 |
It is not possible to add a view deriver under the ``mapped_view`` as the |
fdd1f8
|
1811 |
:term:`view mapper` is intimately tied to the signature of the user-defined |
MM |
1812 |
:term:`view callable`. If you simply need to know what the original view |
|
1813 |
callable was, it can be found as ``info.original_view`` on the provided |
|
1814 |
:class:`pyramid.interfaces.IViewDeriverInfo` object passed to every view |
|
1815 |
deriver. |
a11694
|
1816 |
|
MM |
1817 |
.. warning:: |
|
1818 |
|
c231d8
|
1819 |
The default constraints for any view deriver are ``over='rendered_view'`` |
MM |
1820 |
and ``under='decorated_view'``. When escaping these constraints you must |
|
1821 |
take care to avoid cyclic dependencies between derivers. For example, if |
|
1822 |
you want to add a new view deriver before ``secured_view`` then |
|
1823 |
simply specifying ``over='secured_view'`` is not enough, because the |
|
1824 |
default is also under ``decorated view`` there will be an unsatisfiable |
|
1825 |
cycle. You must specify a valid ``under`` constraint as well, such as |
|
1826 |
``under=INGRESS`` to fall between INGRESS and ``secured_view`` at the |
|
1827 |
beginning of the view pipeline. |