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