summaryrefslogtreecommitdiff
path: root/docs/narr/events.rst
blob: a0142f18747bf4b495ab0cdac022bd760bbff53e (plain)
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
.. index::
   single: event
   single: events
   single: subscriber
   single: INewRequest
   single: INewResponse
   pair: subscriber; ZCML directive

.. _events_chapter:

Using Events
=============

An *event* is an object broadcast by the :mod:`repoze.bfg` framework
at interesting points during the lifetime of an application.  You
don't need to use events in order to create most :mod:`repoze.bfg`
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 :mod:`repoze.bfg` 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:`repoze.bfg.configurator.Configurator.add_subscriber` method to
register the subscriber imperatively, or you'll need to use ZCML for
the same purpose:

.. topic:: Configuring an Event Listener Imperatively

   You can imperatively configure a subscriber function to be called
   for some event type via the
   :meth:`repoze.bfg.configuration.Configurator.add_subscriber`
   method (see also :term:`Configurator`):

   .. code-block:: python
      :linenos:

      from repoze.bfg.interfaces import INewRequest

      from subscribers import mysubscriber

      # "config" below is assumed to be an instance of a 
      # repoze.bfg.configuration.Configurator object

      config.add_subscriber(mysubscriber, INewRequest)

   The first argument to
   :meth:`repoze.bfg.configuration.Configurator.add_subscriber` is the
   subscriber function; the second argument is the event type.

.. topic:: Configuring an Event Listener Through ZCML

   You can configure an event listener by modifying your application's
   ``configure.zcml``.  Here's an example of a bit of XML you can add
   to the ``configure.zcml`` file which registers the above
   ``mysubscriber`` function, which we assume lives in a
   ``subscribers.py`` module within your application:

   .. code-block:: xml
      :linenos:

      <subscriber
         for="repoze.bfg.interfaces.INewRequest"
         handler=".subscribers.mysubscriber"
       />

   See also :ref:`subscriber_directive`.

Either of the above registration examples implies that every time the
:mod:`repoze.bfg` framework emits an event object that supplies an
:class:`repoze.bfg.interfaces.INewRequest` interface, the
``mysubscriber`` function will be called with an *event* object.

As you can see, a subscription is made in terms of an
:term:`interface`.  The event object sent to a subscriber will always
be an object that possesses an interface.  The interface itself
provides documentation of what attributes of the event are available.

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 another.

All the concrete :mod:`repoze.bfg` 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 ZCML to your application's
``configure.zcml`` file:

.. code-block:: xml
   :linenos:

   <subscriber
      for="repoze.bfg.interfaces.INewRequest"
      handler=".subscribers.handle_new_request"
    />

   <subscriber
      for="repoze.bfg.interfaces.INewResponse"
      handler=".subscribers.handle_new_response"
    />

If you're not using ZCML, the
:meth:`repoze.bfg.configuration.Configurator.add_subscriber` method
can alternately be used to perform the same job:

.. ignore-next-block
.. code-block:: python
   :linenos:

   from repoze.bfg.interfaces import INewRequest
   from repoze.bfg.interfaces import INewResponse

   from subscribers import handle_new_request
   from subscribers import handle_new_response

   # "config" below is assumed to be an instance of a 
   # repoze.bfg.configuration.Configurator object

   config.add_subscriber(handle_new_request, INewRequest)
   config.add_subscriber(handle_new_response, INewResponse)

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:`repoze.bfg.interfaces.INewRequest` event objects
have a ``request`` attribute, which is a :term:`request` object,
because the interface defined at
:class:`repoze.bfg.interfaces.INewRequest` says it must.  Likewise, we
know that :class:`repoze.bfg.interfaces.INewResponse` events have a
``response`` attribute, which is a response object constructed by your
application, because the interface defined at
:class:`repoze.bfg.interfaces.INewResponse` says it must.