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
|