import venusian
from zope.interface import (
implementer,
Interface
)
from pyramid.interfaces import (
IContextFound,
INewRequest,
INewResponse,
IApplicationCreated,
IBeforeRender,
)
[docs]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.
"""
venusian = venusian # for unit testing
def __init__(self, *ifaces, **predicates):
self.ifaces = ifaces
self.predicates = predicates
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='pyramid')
return wrapped
@implementer(INewRequest)
[docs]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)
[docs]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(IContextFound)
[docs]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)
[docs]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)
[docs]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