import venusian from zope.interface import implements from repoze.bfg.interfaces import IContextFound from repoze.bfg.interfaces import INewRequest from repoze.bfg.interfaces import INewResponse from repoze.bfg.interfaces import IApplicationCreated from repoze.bfg.interfaces import IFinishedRequest 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`` to the decorator constructor. For example: .. code-block:: python from repoze.bfg.interfaces import INewRequest from repoze.bfg.events import subscriber @subscriber(INewRequest) def mysubscriber(event): event.request.foo = 1 More than one event type can be passed as a construtor argument: .. code-block:: python from repoze.bfg.interfaces import INewRequest from repoze.bfg.events import subscriber @subscriber(INewRequest, INewResponse) 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 repoze.bfg.interfaces import INewRequest from repoze.bfg.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 repoze.bfg.configuration import Configurator config = Configurator() config.scan('somepackage_containing_subscribers') """ venusian = venusian # for unit testing def __init__(self, *ifaces): self.ifaces = ifaces def register(self, scanner, name, wrapped): config = scanner.config config.add_subscriber(wrapped, self.ifaces) def __call__(self, wrapped): self.venusian.attach(wrapped, self.register, category='bfg') return wrapped class NewRequest(object): """ An instance of this class is emitted as an :term:`event` whenever :mod:`repoze.bfg` begins to process a new request. The even instance has an attribute, ``request``, which is a :term:`request` object. This event class implements the :class:`repoze.bfg.interfaces.INewRequest` interface.""" implements(INewRequest) def __init__(self, request): self.request = request class NewResponse(object): """ An instance of this class is emitted as an :term:`event` whenever any :mod:`repoze.bfg` :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, the request will not have this attribute. 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:`repoze.bfg.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:`repoze.bfg.interfaces.INewResponse` event. The :class:`repoze.bfg.interfaces.INewResponse` event exists almost purely for symmetry with the :class:`repoze.bfg.interfaces.INewRequest` event. """ implements(INewResponse) def __init__(self, request, response): self.request = request self.response = response class ContextFound(object): implements(IContextFound) """ An instance of this class is emitted as an :term:`event` after the :mod:`repoze.bfg` :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 :mod:`repoze.bfg`. 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:`repoze.bfg.interfaces.IContextFound` interface. .. note:: As of :mod:`repoze.bfg` 1.3, for backwards compatibility purposes, this event may also be imported as :class:`repoze.bfg.events.AfterTraversal`. """ def __init__(self, request): self.request = request AfterTraversal = ContextFound # b/c as of 1.3 class ApplicationCreated(object): """ An instance of this class is emitted as an :term:`event` when the :meth:`repoze.bfg.configuration.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:`repoze.bfg.interfaces.IApplicationCreated` interface. .. note:: For backwards compatibility purposes, this class can also be imported as :class:`repoze.bfg.events.WSGIApplicationCreatedEvent`. This was the name of the event class before :mod:`repoze.bfg` 1.3. """ implements(IApplicationCreated) def __init__(self, app): self.app = app self.object = app WSGIApplicationCreatedEvent = ApplicationCreated # b/c (as of 1.3) class FinishedRequest(object): """ This :term:`event` is sent after all request processing is finished. An event of this type is emitted unconditionally at the end of request processing, even when an unhandled exception occurs. This is in contrast to the :class:`repoze.bfg.interfaces.INewResponse` event, which cannot be emitted when, due to an unhandled exception, a response object cannot not be created . The :class:`repoze.bfg.events.FinishedRequest` event will even be sent when a request cannot not be created due to an error in request factory code: in such a case, the ``request`` attribute of the event will be ``None``. Mutating the attached ``request`` object in a subscriber to this event will have no effect, because, when this event is emitted, there is no further request or response processing to be done. It is purely an informational event, which can be hooked to do 'finally:'-style tear-down at the end of each request. Instances of this event have an attribute, ``request``, which is the :term:`request` object (or, in extremely rare cases might be ``None``, when a request object cannot be created due to a bug in a request factory) . Because this event happens unconditionally, the set of attributes possessed by an attached ``request`` object are indeterminate. At very least, if the request is not ``None``, it will have a ``registry`` attribute. However, if an exception was thrown before this event is broadcast, it may not have other :mod:`repoze.bfg` -specific attributes such as ``subpath``, ``root`, ``traversed``, etc. Exceptions raised by subscribers of this event are unhandled. This class implements the :class:`repoze.bfg.interfaces.IFinishedRequest` interface. .. note:: This event type is new as of :mod:`repoze.bfg` 1.3. """ implements(IFinishedRequest) def __init__(self, request): self.request = request