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