commit | author | age
|
f07765
|
1 |
import venusian |
CM |
2 |
|
0c29cf
|
3 |
from zope.interface import implementer, Interface |
a0423a
|
4 |
|
0c1c39
|
5 |
from pyramid.interfaces import ( |
CM |
6 |
IContextFound, |
|
7 |
INewRequest, |
|
8 |
INewResponse, |
|
9 |
IApplicationCreated, |
|
10 |
IBeforeRender, |
d4f5a8
|
11 |
IBeforeTraversal, |
0c29cf
|
12 |
) |
MM |
13 |
|
a0423a
|
14 |
|
f07765
|
15 |
class subscriber(object): |
95f766
|
16 |
""" Decorator activated via a :term:`scan` which treats the function |
CM |
17 |
being decorated as an event subscriber for the set of interfaces passed |
|
18 |
as ``*ifaces`` and the set of predicate terms passed as ``**predicates`` |
|
19 |
to the decorator constructor. |
f07765
|
20 |
|
CM |
21 |
For example: |
|
22 |
|
|
23 |
.. code-block:: python |
012b97
|
24 |
|
6067de
|
25 |
from pyramid.events import NewRequest |
c81aad
|
26 |
from pyramid.events import subscriber |
f07765
|
27 |
|
6067de
|
28 |
@subscriber(NewRequest) |
f07765
|
29 |
def mysubscriber(event): |
CM |
30 |
event.request.foo = 1 |
|
31 |
|
52a948
|
32 |
More than one event type can be passed as a constructor argument. The |
b3a692
|
33 |
decorated subscriber will be called for each event type. |
012b97
|
34 |
|
f07765
|
35 |
.. code-block:: python |
012b97
|
36 |
|
6067de
|
37 |
from pyramid.events import NewRequest, NewResponse |
c81aad
|
38 |
from pyramid.events import subscriber |
f07765
|
39 |
|
6067de
|
40 |
@subscriber(NewRequest, NewResponse) |
f07765
|
41 |
def mysubscriber(event): |
edfc4f
|
42 |
print(event) |
f07765
|
43 |
|
CM |
44 |
When the ``subscriber`` decorator is used without passing an arguments, |
|
45 |
the function it decorates is called for every event sent: |
|
46 |
|
|
47 |
.. code-block:: python |
012b97
|
48 |
|
c81aad
|
49 |
from pyramid.events import subscriber |
f07765
|
50 |
|
CM |
51 |
@subscriber() |
|
52 |
def mysubscriber(event): |
edfc4f
|
53 |
print(event) |
f07765
|
54 |
|
CM |
55 |
This method will have no effect until a :term:`scan` is performed |
|
56 |
against the package or module which contains it, ala: |
|
57 |
|
|
58 |
.. code-block:: python |
012b97
|
59 |
|
aff443
|
60 |
from pyramid.config import Configurator |
f07765
|
61 |
config = Configurator() |
CM |
62 |
config.scan('somepackage_containing_subscribers') |
|
63 |
|
95f766
|
64 |
Any ``**predicate`` arguments will be passed along to |
CM |
65 |
:meth:`pyramid.config.Configurator.add_subscriber`. See |
|
66 |
:ref:`subscriber_predicates` for a description of how predicates can |
|
67 |
narrow the set of circumstances in which a subscriber will be called. |
|
68 |
|
498158
|
69 |
Two additional keyword arguments which will be passed to the |
MM |
70 |
:term:`venusian` ``attach`` function are ``_depth`` and ``_category``. |
|
71 |
|
|
72 |
``_depth`` is provided for people who wish to reuse this class from another |
|
73 |
decorator. The default value is ``0`` and should be specified relative to |
|
74 |
the ``subscriber`` invocation. It will be passed in to the |
|
75 |
:term:`venusian` ``attach`` function as the depth of the callstack when |
|
76 |
Venusian checks if the decorator is being used in a class or module |
|
77 |
context. It's not often used, but it can be useful in this circumstance. |
|
78 |
|
|
79 |
``_category`` sets the decorator category name. It can be useful in |
|
80 |
combination with the ``category`` argument of ``scan`` to control which |
|
81 |
views should be processed. |
|
82 |
|
|
83 |
See the :py:func:`venusian.attach` function in Venusian for more |
|
84 |
information about the ``_depth`` and ``_category`` arguments. |
|
85 |
|
|
86 |
.. versionchanged:: 1.9.1 |
|
87 |
Added the ``_depth`` and ``_category`` arguments. |
|
88 |
|
f07765
|
89 |
""" |
0c29cf
|
90 |
|
MM |
91 |
venusian = venusian # for unit testing |
f07765
|
92 |
|
95f766
|
93 |
def __init__(self, *ifaces, **predicates): |
f07765
|
94 |
self.ifaces = ifaces |
95f766
|
95 |
self.predicates = predicates |
498158
|
96 |
self.depth = predicates.pop('_depth', 0) |
MM |
97 |
self.category = predicates.pop('_category', 'pyramid') |
f07765
|
98 |
|
CM |
99 |
def register(self, scanner, name, wrapped): |
|
100 |
config = scanner.config |
52a948
|
101 |
for iface in self.ifaces or (Interface,): |
95f766
|
102 |
config.add_subscriber(wrapped, iface, **self.predicates) |
f07765
|
103 |
|
CM |
104 |
def __call__(self, wrapped): |
0c29cf
|
105 |
self.venusian.attach( |
MM |
106 |
wrapped, |
|
107 |
self.register, |
|
108 |
category=self.category, |
|
109 |
depth=self.depth + 1, |
|
110 |
) |
f07765
|
111 |
return wrapped |
0c29cf
|
112 |
|
f07765
|
113 |
|
3b7334
|
114 |
@implementer(INewRequest) |
a0423a
|
115 |
class NewRequest(object): |
8b1f6e
|
116 |
""" An instance of this class is emitted as an :term:`event` |
fd5ae9
|
117 |
whenever :app:`Pyramid` begins to process a new request. The |
56cfe9
|
118 |
event instance has an attribute, ``request``, which is a |
8f45be
|
119 |
:term:`request` object. This event class implements the |
c81aad
|
120 |
:class:`pyramid.interfaces.INewRequest` interface.""" |
0c29cf
|
121 |
|
a0423a
|
122 |
def __init__(self, request): |
CM |
123 |
self.request = request |
0c29cf
|
124 |
|
a0423a
|
125 |
|
3b7334
|
126 |
@implementer(INewResponse) |
a0423a
|
127 |
class NewResponse(object): |
8b1f6e
|
128 |
""" An instance of this class is emitted as an :term:`event` |
fd5ae9
|
129 |
whenever any :app:`Pyramid` :term:`view` or :term:`exception |
8f45be
|
130 |
view` returns a :term:`response`. |
eb7ea4
|
131 |
|
CM |
132 |
The instance has two attributes:``request``, which is the request |
|
133 |
which caused the response, and ``response``, which is the response |
|
134 |
object returned by a view or renderer. |
8f45be
|
135 |
|
0ffb09
|
136 |
If the ``response`` was generated by an :term:`exception view`, the |
CM |
137 |
request will have an attribute named ``exception``, which is the |
|
138 |
exception object which caused the exception view to be executed. If the |
|
139 |
response was generated by a 'normal' view, this attribute of the request |
|
140 |
will be ``None``. |
8f45be
|
141 |
|
0ffb09
|
142 |
This event will not be generated if a response cannot be created due to |
CM |
143 |
an exception that is not caught by an exception view (no response is |
|
144 |
created under this circumstace). |
eb7ea4
|
145 |
|
CM |
146 |
This class implements the |
c81aad
|
147 |
:class:`pyramid.interfaces.INewResponse` interface. |
9ec2d6
|
148 |
|
CM |
149 |
.. note:: |
|
150 |
|
|
151 |
Postprocessing a response is usually better handled in a WSGI |
|
152 |
:term:`middleware` component than in subscriber code that is |
c81aad
|
153 |
called by a :class:`pyramid.interfaces.INewResponse` event. |
CM |
154 |
The :class:`pyramid.interfaces.INewResponse` event exists |
9ec2d6
|
155 |
almost purely for symmetry with the |
c81aad
|
156 |
:class:`pyramid.interfaces.INewRequest` event. |
9ec2d6
|
157 |
""" |
0c29cf
|
158 |
|
eb7ea4
|
159 |
def __init__(self, request, response): |
CM |
160 |
self.request = request |
a0423a
|
161 |
self.response = response |
0c29cf
|
162 |
|
faaebc
|
163 |
|
d4f5a8
|
164 |
@implementer(IBeforeTraversal) |
BJR |
165 |
class BeforeTraversal(object): |
499b78
|
166 |
""" |
DS |
167 |
An instance of this class is emitted as an :term:`event` after the |
3f34aa
|
168 |
:app:`Pyramid` :term:`router` has attempted to find a :term:`route` object |
BJR |
169 |
but before any traversal or view code is executed. The instance has an |
|
170 |
attribute, ``request``, which is the request object generated by |
|
171 |
:app:`Pyramid`. |
499b78
|
172 |
|
3f34aa
|
173 |
Notably, the request object **may** have an attribute named |
BJR |
174 |
``matched_route``, which is the matched route if found. If no route |
|
175 |
matched, this attribute is not available. |
499b78
|
176 |
|
d4f5a8
|
177 |
This class implements the :class:`pyramid.interfaces.IBeforeTraversal` |
499b78
|
178 |
interface. |
DS |
179 |
""" |
|
180 |
|
|
181 |
def __init__(self, request): |
|
182 |
self.request = request |
0c29cf
|
183 |
|
499b78
|
184 |
|
3b7334
|
185 |
@implementer(IContextFound) |
8f45be
|
186 |
class ContextFound(object): |
8b1f6e
|
187 |
""" An instance of this class is emitted as an :term:`event` after |
fd5ae9
|
188 |
the :app:`Pyramid` :term:`router` finds a :term:`context` |
8f45be
|
189 |
object (after it performs traversal) but before any view code is |
CM |
190 |
executed. The instance has an attribute, ``request``, which is |
fd5ae9
|
191 |
the request object generated by :app:`Pyramid`. |
8f45be
|
192 |
|
CM |
193 |
Notably, the request object will have an attribute named |
|
194 |
``context``, which is the context that will be provided to the |
|
195 |
view which will eventually be called, as well as other attributes |
|
196 |
attached by context-finding code. |
|
197 |
|
|
198 |
This class implements the |
c81aad
|
199 |
:class:`pyramid.interfaces.IContextFound` interface. |
8f45be
|
200 |
|
012b97
|
201 |
.. note:: |
M |
202 |
|
|
203 |
As of :app:`Pyramid` 1.0, for backwards compatibility purposes, this |
|
204 |
event may also be imported as :class:`pyramid.events.AfterTraversal`. |
8f45be
|
205 |
""" |
0c29cf
|
206 |
|
49304c
|
207 |
def __init__(self, request): |
CM |
208 |
self.request = request |
8f45be
|
209 |
|
0c29cf
|
210 |
|
MM |
211 |
AfterTraversal = ContextFound # b/c as of 1.0 |
|
212 |
|
012b97
|
213 |
|
3b7334
|
214 |
@implementer(IApplicationCreated) |
3f34aa
|
215 |
class ApplicationCreated(object): |
8b1f6e
|
216 |
""" An instance of this class is emitted as an :term:`event` when |
aff443
|
217 |
the :meth:`pyramid.config.Configurator.make_wsgi_app` is |
640044
|
218 |
called. The instance has an attribute, ``app``, which is an |
83df8b
|
219 |
instance of the :term:`router` that will handle WSGI requests. |
CM |
220 |
This class implements the |
c81aad
|
221 |
:class:`pyramid.interfaces.IApplicationCreated` interface. |
8f45be
|
222 |
|
012b97
|
223 |
.. note:: |
8f45be
|
224 |
|
012b97
|
225 |
For backwards compatibility purposes, this class can also be imported as |
M |
226 |
:class:`pyramid.events.WSGIApplicationCreatedEvent`. This was the name |
|
227 |
of the event class before :app:`Pyramid` 1.0. |
8f45be
|
228 |
""" |
0c29cf
|
229 |
|
faaebc
|
230 |
def __init__(self, app): |
CM |
231 |
self.app = app |
49304c
|
232 |
self.object = app |
51dcd9
|
233 |
|
0c29cf
|
234 |
|
MM |
235 |
WSGIApplicationCreatedEvent = ApplicationCreated # b/c (as of 1.0) |
|
236 |
|
8f45be
|
237 |
|
3b7334
|
238 |
@implementer(IBeforeRender) |
a76e99
|
239 |
class BeforeRender(dict): |
CM |
240 |
""" |
438277
|
241 |
Subscribers to this event may introspect and modify the set of |
a76e99
|
242 |
:term:`renderer globals` before they are passed to a :term:`renderer`. |
1a928d
|
243 |
This event object itself has a dictionary-like interface that can be used |
a76e99
|
244 |
for this purpose. For example:: |
CM |
245 |
|
f32f32
|
246 |
from pyramid.events import subscriber |
5c52da
|
247 |
from pyramid.events import BeforeRender |
a76e99
|
248 |
|
5c52da
|
249 |
@subscriber(BeforeRender) |
a76e99
|
250 |
def add_global(event): |
CM |
251 |
event['mykey'] = 'foo' |
|
252 |
|
|
253 |
An object of this type is sent as an event just before a :term:`renderer` |
c6601f
|
254 |
is invoked. |
a76e99
|
255 |
|
438277
|
256 |
If a subscriber adds a key via ``__setitem__`` that already exists in |
CDLG |
257 |
the renderer globals dictionary, it will overwrite the older value there. |
|
258 |
This can be problematic because event subscribers to the BeforeRender |
|
259 |
event do not possess any relative ordering. For maximum interoperability |
|
260 |
with other third-party subscribers, if you write an event subscriber meant |
|
261 |
to be used as a BeforeRender subscriber, your subscriber code will need to |
|
262 |
ensure no value already exists in the renderer globals dictionary before |
|
263 |
setting an overriding value (which can be done using ``.get`` or |
|
264 |
``__contains__`` of the event object). |
a76e99
|
265 |
|
a31924
|
266 |
The dictionary returned from the view is accessible through the |
JC |
267 |
:attr:`rendering_val` attribute of a :class:`~pyramid.events.BeforeRender` |
2516df
|
268 |
event. |
JC |
269 |
|
|
270 |
Suppose you return ``{'mykey': 'somevalue', 'mykey2': 'somevalue2'}`` from |
|
271 |
your view callable, like so:: |
|
272 |
|
|
273 |
from pyramid.view import view_config |
|
274 |
|
|
275 |
@view_config(renderer='some_renderer') |
|
276 |
def myview(request): |
|
277 |
return {'mykey': 'somevalue', 'mykey2': 'somevalue2'} |
|
278 |
|
|
279 |
:attr:`rendering_val` can be used to access these values from the |
|
280 |
:class:`~pyramid.events.BeforeRender` object:: |
a31924
|
281 |
|
JC |
282 |
from pyramid.events import subscriber |
|
283 |
from pyramid.events import BeforeRender |
|
284 |
|
|
285 |
@subscriber(BeforeRender) |
|
286 |
def read_return(event): |
2516df
|
287 |
# {'mykey': 'somevalue'} is returned from the view |
a31924
|
288 |
print(event.rendering_val['mykey']) |
JC |
289 |
|
03403e
|
290 |
In other words, :attr:`rendering_val` is the (non-system) value returned |
CM |
291 |
by a view or passed to ``render*`` as ``value``. This feature is new in |
|
292 |
Pyramid 1.2. |
a31924
|
293 |
|
b91ca3
|
294 |
For a description of the values present in the renderer globals dictionary, |
CDLG |
295 |
see :ref:`renderer_system_values`. |
9a66aa
|
296 |
|
2033ee
|
297 |
.. seealso:: |
SP |
298 |
|
|
299 |
See also :class:`pyramid.interfaces.IBeforeRender`. |
5c52da
|
300 |
""" |
0c29cf
|
301 |
|
9a66aa
|
302 |
def __init__(self, system, rendering_val=None): |
5c52da
|
303 |
dict.__init__(self, system) |
9a66aa
|
304 |
self.rendering_val = rendering_val |