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
|
.. index::
single: event
single: subscriber
single: INewRequest
single: INewResponse
single: NewRequest
single: NewResponse
.. _events_chapter:
Using Events
============
An *event* is an object broadcast by the :app:`Pyramid` framework at
interesting points during the lifetime of an application. You don't need to
use events in order to create most :app:`Pyramid` applications, but they can be
useful when you want to perform slightly advanced operations. For example,
subscribing to an event can allow you to run some code as the result of every
new request.
Events in :app:`Pyramid` are always broadcast by the framework. However, they
only become useful when you register a *subscriber*. A subscriber is a
function that accepts a single argument named `event`:
.. code-block:: python
:linenos:
def mysubscriber(event):
print(event)
The above is a subscriber that simply prints the event to the console when it's
called.
The mere existence of a subscriber function, however, is not sufficient to
arrange for it to be called. To arrange for the subscriber to be called,
you'll need to use the :meth:`pyramid.config.Configurator.add_subscriber`
method or you'll need to use the :func:`pyramid.events.subscriber` decorator to
decorate a function found via a :term:`scan`.
Configuring an Event Listener Imperatively
------------------------------------------
You can imperatively configure a subscriber function to be called for some
event type via the :meth:`~pyramid.config.Configurator.add_subscriber` method:
.. code-block:: python
:linenos:
from pyramid.events import NewRequest
from subscribers import mysubscriber
# "config" below is assumed to be an instance of a
# pyramid.config.Configurator object
config.add_subscriber(mysubscriber, NewRequest)
The first argument to :meth:`~pyramid.config.Configurator.add_subscriber` is
the subscriber function (or a :term:`dotted Python name` which refers to a
subscriber callable); the second argument is the event type.
.. seealso::
See also :term:`Configurator`.
Configuring an Event Listener Using a Decorator
-----------------------------------------------
You can configure a subscriber function to be called for some event type via
the :func:`pyramid.events.subscriber` function.
.. code-block:: python
:linenos:
from pyramid.events import NewRequest
from pyramid.events import subscriber
@subscriber(NewRequest)
def mysubscriber(event):
event.request.foo = 1
When the :func:`~pyramid.events.subscriber` decorator is used, a :term:`scan`
must be performed against the package containing the decorated function for the
decorator to have any effect.
Either of the above registration examples implies that every time the
:app:`Pyramid` framework emits an event object that supplies an
:class:`pyramid.events.NewRequest` interface, the ``mysubscriber`` function
will be called with an *event* object.
As you can see, a subscription is made in terms of a *class* (such as
:class:`pyramid.events.NewResponse`). The event object sent to a subscriber
will always be an object that possesses an :term:`interface`. For
:class:`pyramid.events.NewResponse`, that interface is
:class:`pyramid.interfaces.INewResponse`. The interface documentation provides
information about available attributes and methods of the event objects.
The return value of a subscriber function is ignored. Subscribers to the same
event type are not guaranteed to be called in any particular order relative to
each other.
All the concrete :app:`Pyramid` event types are documented in the
:ref:`events_module` API documentation.
An Example
----------
If you create event listener functions in a ``subscribers.py`` file in your
application like so:
.. code-block:: python
:linenos:
def handle_new_request(event):
print('request', event.request)
def handle_new_response(event):
print('response', event.response)
You may configure these functions to be called at the appropriate times by
adding the following code to your application's configuration startup:
.. code-block:: python
:linenos:
# config is an instance of pyramid.config.Configurator
config.add_subscriber('myproject.subscribers.handle_new_request',
'pyramid.events.NewRequest')
config.add_subscriber('myproject.subscribers.handle_new_response',
'pyramid.events.NewResponse')
Either mechanism causes the functions in ``subscribers.py`` to be registered as
event subscribers. Under this configuration, when the application is run, each
time a new request or response is detected, a message will be printed to the
console.
Each of our subscriber functions accepts an ``event`` object and prints an
attribute of the event object. This begs the question: how can we know which
attributes a particular event has?
We know that :class:`pyramid.events.NewRequest` event objects have a
``request`` attribute, which is a :term:`request` object, because the interface
defined at :class:`pyramid.interfaces.INewRequest` says it must. Likewise, we
know that :class:`pyramid.interfaces.NewResponse` events have a ``response``
attribute, which is a response object constructed by your application, because
the interface defined at :class:`pyramid.interfaces.INewResponse` says it must
(:class:`pyramid.events.NewResponse` objects also have a ``request``).
.. _custom_events:
Creating Your Own Events
------------------------
In addition to using the events that the Pyramid framework creates, you can
create your own events for use in your application. This can be useful to
decouple parts of your application.
For example, suppose your application has to do many things when a new document
is created. Rather than putting all this logic in the view that creates the
document, you can create the document in your view and then fire a custom
event. Subscribers to the custom event can take other actions, such as indexing
the document, sending email, or sending a message to a remote system.
An event is simply an object. There are no required attributes or method for
your custom events. In general, your events should keep track of the
information that subscribers will need. Here are some example custom event
classes:
.. code-block:: python
:linenos:
class DocCreated(object):
def __init__(self, doc, request):
self.doc = doc
self.request = request
class UserEvent(object):
def __init__(self, user):
self.user = user
class UserLoggedIn(UserEvent):
pass
Some Pyramid applications choose to define custom events classes in an
``events`` module.
You can subscribe to custom events in the same way that you subscribe to
Pyramid events—either imperatively or with a decorator. You can also use custom
events with :ref:`subscriber predicates <subscriber_predicates>`. Here's an
example of subscribing to a custom event with a decorator:
.. code-block:: python
:linenos:
from pyramid.events import subscriber
from .events import DocCreated
from .index import index_doc
@subscriber(DocCreated)
def index_doc(event):
# index the document using our application's index_doc function
index_doc(event.doc, event.request)
The above example assumes that the application defines a ``DocCreated`` event
class and an ``index_doc`` function.
To fire your custom events use the :meth:`pyramid.registry.Registry.notify`
method, which is most often accessed as ``request.registry.notify``. For
example:
.. code-block:: python
:linenos:
from .events import DocCreated
def new_doc_view(request):
doc = MyDoc()
event = DocCreated(doc, request)
request.registry.notify(event)
return {'document': doc}
This example view will notify all subscribers to the custom ``DocCreated``
event.
Note that when you fire an event, all subscribers are run synchronously so it's
generally not a good idea to create event handlers that may take a long time to
run. Although event handlers could be used as a central place to spawn tasks on
your own message queues.
|