Steve Piercy
2018-09-22 e22970cd21eb36c2a658c843bb5cb4f59d77fd19
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
f07765 71     """
CM 72     venusian = venusian # for unit testing
73
95f766 74     def __init__(self, *ifaces, **predicates):
f07765 75         self.ifaces = ifaces
95f766 76         self.predicates = predicates
f07765 77
CM 78     def register(self, scanner, name, wrapped):
79         config = scanner.config
52a948 80         for iface in self.ifaces or (Interface,):
95f766 81             config.add_subscriber(wrapped, iface, **self.predicates)
f07765 82
CM 83     def __call__(self, wrapped):
cba2e1 84         self.venusian.attach(wrapped, self.register, category='pyramid')
f07765 85         return wrapped
CM 86
3b7334 87 @implementer(INewRequest)
a0423a 88 class NewRequest(object):
8b1f6e 89     """ An instance of this class is emitted as an :term:`event`
fd5ae9 90     whenever :app:`Pyramid` begins to process a new request.  The
56cfe9 91     event instance has an attribute, ``request``, which is a
8f45be 92     :term:`request` object.  This event class implements the
c81aad 93     :class:`pyramid.interfaces.INewRequest` interface."""
a0423a 94     def __init__(self, request):
CM 95         self.request = request
96
3b7334 97 @implementer(INewResponse)
a0423a 98 class NewResponse(object):
8b1f6e 99     """ An instance of this class is emitted as an :term:`event`
fd5ae9 100     whenever any :app:`Pyramid` :term:`view` or :term:`exception
8f45be 101     view` returns a :term:`response`.
eb7ea4 102
CM 103     The instance has two attributes:``request``, which is the request
104     which caused the response, and ``response``, which is the response
105     object returned by a view or renderer.
8f45be 106
0ffb09 107     If the ``response`` was generated by an :term:`exception view`, the
CM 108     request will have an attribute named ``exception``, which is the
109     exception object which caused the exception view to be executed.  If the
110     response was generated by a 'normal' view, this attribute of the request
111     will be ``None``.
8f45be 112
0ffb09 113     This event will not be generated if a response cannot be created due to
CM 114     an exception that is not caught by an exception view (no response is
115     created under this circumstace).
eb7ea4 116
CM 117     This class implements the
c81aad 118     :class:`pyramid.interfaces.INewResponse` interface.
9ec2d6 119
CM 120     .. note::
121
122        Postprocessing a response is usually better handled in a WSGI
123        :term:`middleware` component than in subscriber code that is
c81aad 124        called by a :class:`pyramid.interfaces.INewResponse` event.
CM 125        The :class:`pyramid.interfaces.INewResponse` event exists
9ec2d6 126        almost purely for symmetry with the
c81aad 127        :class:`pyramid.interfaces.INewRequest` event.
9ec2d6 128     """
eb7ea4 129     def __init__(self, request, response):
CM 130         self.request = request
a0423a 131         self.response = response
faaebc 132
d4f5a8 133 @implementer(IBeforeTraversal)
BJR 134 class BeforeTraversal(object):
499b78 135     """
DS 136     An instance of this class is emitted as an :term:`event` after the
3f34aa 137     :app:`Pyramid` :term:`router` has attempted to find a :term:`route` object
BJR 138     but before any traversal or view code is executed. The instance has an
139     attribute, ``request``, which is the request object generated by
140     :app:`Pyramid`.
499b78 141
3f34aa 142     Notably, the request object **may** have an attribute named
BJR 143     ``matched_route``, which is the matched route if found. If no route
144     matched, this attribute is not available.
499b78 145
d4f5a8 146     This class implements the :class:`pyramid.interfaces.IBeforeTraversal`
499b78 147     interface.
DS 148     """
149
150     def __init__(self, request):
151         self.request = request
152
3b7334 153 @implementer(IContextFound)
8f45be 154 class ContextFound(object):
8b1f6e 155     """ An instance of this class is emitted as an :term:`event` after
fd5ae9 156     the :app:`Pyramid` :term:`router` finds a :term:`context`
8f45be 157     object (after it performs traversal) but before any view code is
CM 158     executed.  The instance has an attribute, ``request``, which is
fd5ae9 159     the request object generated by :app:`Pyramid`.
8f45be 160
CM 161     Notably, the request object will have an attribute named
162     ``context``, which is the context that will be provided to the
163     view which will eventually be called, as well as other attributes
164     attached by context-finding code.
165
166     This class implements the
c81aad 167     :class:`pyramid.interfaces.IContextFound` interface.
8f45be 168
012b97 169     .. note::
M 170
171        As of :app:`Pyramid` 1.0, for backwards compatibility purposes, this
172        event may also be imported as :class:`pyramid.events.AfterTraversal`.
8f45be 173     """
49304c 174     def __init__(self, request):
CM 175         self.request = request
8f45be 176
c81aad 177 AfterTraversal = ContextFound # b/c as of 1.0
012b97 178
3b7334 179 @implementer(IApplicationCreated)
3f34aa 180 class ApplicationCreated(object):
8b1f6e 181     """ An instance of this class is emitted as an :term:`event` when
aff443 182     the :meth:`pyramid.config.Configurator.make_wsgi_app` is
640044 183     called.  The instance has an attribute, ``app``, which is an
83df8b 184     instance of the :term:`router` that will handle WSGI requests.
CM 185     This class implements the
c81aad 186     :class:`pyramid.interfaces.IApplicationCreated` interface.
8f45be 187
012b97 188     .. note::
8f45be 189
012b97 190        For backwards compatibility purposes, this class can also be imported as
M 191        :class:`pyramid.events.WSGIApplicationCreatedEvent`.  This was the name
192        of the event class before :app:`Pyramid` 1.0.
8f45be 193     """
faaebc 194     def __init__(self, app):
CM 195         self.app = app
49304c 196         self.object = app
51dcd9 197
c81aad 198 WSGIApplicationCreatedEvent = ApplicationCreated # b/c (as of 1.0)
8f45be 199
3b7334 200 @implementer(IBeforeRender)
a76e99 201 class BeforeRender(dict):
CM 202     """
438277 203     Subscribers to this event may introspect and modify the set of
a76e99 204     :term:`renderer globals` before they are passed to a :term:`renderer`.
CM 205     This event object iself has a dictionary-like interface that can be used
206     for this purpose.  For example::
207
f32f32 208       from pyramid.events import subscriber
5c52da 209       from pyramid.events import BeforeRender
a76e99 210
5c52da 211       @subscriber(BeforeRender)
a76e99 212       def add_global(event):
CM 213           event['mykey'] = 'foo'
214
215     An object of this type is sent as an event just before a :term:`renderer`
c6601f 216     is invoked.
a76e99 217
438277 218     If a subscriber adds a key via ``__setitem__`` that already exists in
CDLG 219     the renderer globals dictionary, it will overwrite the older value there.
220     This can be problematic because event subscribers to the BeforeRender
221     event do not possess any relative ordering.  For maximum interoperability
222     with other third-party subscribers, if you write an event subscriber meant
223     to be used as a BeforeRender subscriber, your subscriber code will need to
224     ensure no value already exists in the renderer globals dictionary before
225     setting an overriding value (which can be done using ``.get`` or
226     ``__contains__`` of the event object).
a76e99 227
a31924 228     The dictionary returned from the view is accessible through the
JC 229     :attr:`rendering_val` attribute of a :class:`~pyramid.events.BeforeRender`
2516df 230     event.
JC 231
232     Suppose you return ``{'mykey': 'somevalue', 'mykey2': 'somevalue2'}`` from
233     your view callable, like so::
234
235       from pyramid.view import view_config
236
237       @view_config(renderer='some_renderer')
238       def myview(request):
239           return {'mykey': 'somevalue', 'mykey2': 'somevalue2'}
240
241     :attr:`rendering_val` can be used to access these values from the
242     :class:`~pyramid.events.BeforeRender` object::
a31924 243
JC 244       from pyramid.events import subscriber
245       from pyramid.events import BeforeRender
246
247       @subscriber(BeforeRender)
248       def read_return(event):
2516df 249           # {'mykey': 'somevalue'} is returned from the view
a31924 250           print(event.rendering_val['mykey'])
JC 251
03403e 252     In other words, :attr:`rendering_val` is the (non-system) value returned
CM 253     by a view or passed to ``render*`` as ``value``.  This feature is new in
254     Pyramid 1.2.
a31924 255
b91ca3 256     For a description of the values present in the renderer globals dictionary,
CDLG 257     see :ref:`renderer_system_values`.
9a66aa 258
2033ee 259     .. seealso::
SP 260
261         See also :class:`pyramid.interfaces.IBeforeRender`.
5c52da 262     """
9a66aa 263     def __init__(self, system, rendering_val=None):
5c52da 264         dict.__init__(self, system)
9a66aa 265         self.rendering_val = rendering_val
CM 266