Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 353 lines (251 sloc) 12.331 kb
c360f00 @mitsuhiko Added signal documentation
authored
1 .. _signals:
2
3 Signals
4 =======
5
6 .. versionadded:: 0.6
7
8 Starting with Flask 0.6, there is integrated support for signalling in
9 Flask. This support is provided by the excellent `blinker`_ library and
10 will gracefully fall back if it is not available.
11
12 What are signals? Signals help you decouple applications by sending
13 notifications when actions occur elsewhere in the core framework or
14 another Flask extensions. In short, signals allow certain senders to
15 notify subscribers that something happened.
16
17 Flask comes with a couple of signals and other extensions might provide
18 more. Also keep in mind that signals are intended to notify subscribers
19 and should not encourage subscribers to modify data. You will notice that
20 there are signals that appear to do the same thing like some of the
21 builtin decorators do (eg: :data:`~flask.request_started` is very similar
22 to :meth:`~flask.Flask.before_request`). There are however difference in
23 how they work. The core :meth:`~flask.Flask.before_request` handler for
24 example is executed in a specific order and is able to abort the request
25 early by returning a response. In contrast all signal handlers are
26 executed in undefined order and do not modify any data.
27
28 The big advantage of signals over handlers is that you can safely
ffe0c54 @jeffwidman Grammer and readability fixes
jeffwidman authored
29 subscribe to them for just a split second. These temporary
c360f00 @mitsuhiko Added signal documentation
authored
30 subscriptions are helpful for unittesting for example. Say you want to
31 know what templates were rendered as part of a request: signals allow you
32 to do exactly that.
33
34 Subscribing to Signals
35 ----------------------
36
37 To subscribe to a signal, you can use the
38 :meth:`~blinker.base.Signal.connect` method of a signal. The first
39 argument is the function that should be called when the signal is emitted,
40 the optional second argument specifies a sender. To unsubscribe from a
41 signal, you can use the :meth:`~blinker.base.Signal.disconnect` method.
42
43 For all core Flask signals, the sender is the application that issued the
3b0eb0f @mitsuhiko Added notes on proxies
authored
44 signal. When you subscribe to a signal, be sure to also provide a sender
ffe0c54 @jeffwidman Grammer and readability fixes
jeffwidman authored
45 unless you really want to listen for signals from all applications. This is
3b0eb0f @mitsuhiko Added notes on proxies
authored
46 especially true if you are developing an extension.
c360f00 @mitsuhiko Added signal documentation
authored
47
ffe0c54 @jeffwidman Grammer and readability fixes
jeffwidman authored
48 For example, here is a helper context manager that can be used in a unittest
49 to determine which templates were rendered and what variables were passed
c360f00 @mitsuhiko Added signal documentation
authored
50 to the template::
51
52 from flask import template_rendered
53 from contextlib import contextmanager
54
55 @contextmanager
3b0eb0f @mitsuhiko Added notes on proxies
authored
56 def captured_templates(app):
c360f00 @mitsuhiko Added signal documentation
authored
57 recorded = []
174f322 @mitsuhiko Mention that people subscribe with **extra.
authored
58 def record(sender, template, context, **extra):
c360f00 @mitsuhiko Added signal documentation
authored
59 recorded.append((template, context))
3b0eb0f @mitsuhiko Added notes on proxies
authored
60 template_rendered.connect(record, app)
c360f00 @mitsuhiko Added signal documentation
authored
61 try:
2a0e71f @mitsuhiko Fixed a typo in an example in the docs
authored
62 yield recorded
c360f00 @mitsuhiko Added signal documentation
authored
63 finally:
3b0eb0f @mitsuhiko Added notes on proxies
authored
64 template_rendered.disconnect(record, app)
c360f00 @mitsuhiko Added signal documentation
authored
65
66 This can now easily be paired with a test client::
67
3b0eb0f @mitsuhiko Added notes on proxies
authored
68 with captured_templates(app) as templates:
c360f00 @mitsuhiko Added signal documentation
authored
69 rv = app.test_client().get('/')
70 assert rv.status_code == 200
71 assert len(templates) == 1
72 template, context = templates[0]
73 assert template.name == 'index.html'
74 assert len(context['items']) == 10
75
174f322 @mitsuhiko Mention that people subscribe with **extra.
authored
76 Make sure to subscribe with an extra ``**extra`` argument so that your
77 calls don't fail if Flask introduces new arguments to the signals.
78
3b0eb0f @mitsuhiko Added notes on proxies
authored
79 All the template rendering in the code issued by the application `app`
663802e @defuz docs: ``with``, ``for``, ``self``
defuz authored
80 in the body of the ``with`` block will now be recorded in the `templates`
3b0eb0f @mitsuhiko Added notes on proxies
authored
81 variable. Whenever a template is rendered, the template object as well as
82 context are appended to it.
83
84 Additionally there is a convenient helper method
ffe0c54 @jeffwidman Grammer and readability fixes
jeffwidman authored
85 (:meth:`~blinker.base.Signal.connected_to`) that allows you to
75050d4 @7footmoustache Simple documentation corrections, mostly typos.
7footmoustache authored
86 temporarily subscribe a function to a signal with a context manager on
2ac1b7d @mitsuhiko Fixed a documentation error. This fixes #143
authored
87 its own. Because the return value of the context manager cannot be
ffe0c54 @jeffwidman Grammer and readability fixes
jeffwidman authored
88 specified that way, you have to pass the list in as an argument::
3b0eb0f @mitsuhiko Added notes on proxies
authored
89
90 from flask import template_rendered
91
230e136 @mitsuhiko Subscribe to signals with extra kwargs in the docs
authored
92 def captured_templates(app, recorded, **extra):
36a421b @mrluanma Fixed template_rendered example in signal documentation.
mrluanma authored
93 def record(sender, template, context):
3b0eb0f @mitsuhiko Added notes on proxies
authored
94 recorded.append((template, context))
0510867 @mitsuhiko Documented blinker 1.1 changes
authored
95 return template_rendered.connected_to(record, app)
96
2ac1b7d @mitsuhiko Fixed a documentation error. This fixes #143
authored
97 The example above would then look like this::
98
99 templates = []
230e136 @mitsuhiko Subscribe to signals with extra kwargs in the docs
authored
100 with captured_templates(app, templates, **extra):
2ac1b7d @mitsuhiko Fixed a documentation error. This fixes #143
authored
101 ...
102 template, context = templates[0]
103
0510867 @mitsuhiko Documented blinker 1.1 changes
authored
104 .. admonition:: Blinker API Changes
105
106 The :meth:`~blinker.base.Signal.connected_to` method arrived in Blinker
107 with version 1.1.
c360f00 @mitsuhiko Added signal documentation
authored
108
109 Creating Signals
110 ----------------
111
112 If you want to use signals in your own application, you can use the
113 blinker library directly. The most common use case are named signals in a
114 custom :class:`~blinker.base.Namespace`.. This is what is recommended
115 most of the time::
116
117 from blinker import Namespace
118 my_signals = Namespace()
119
120 Now you can create new signals like this::
121
122 model_saved = my_signals.signal('model-saved')
123
124 The name for the signal here makes it unique and also simplifies
125 debugging. You can access the name of the signal with the
126 :attr:`~blinker.base.NamedSignal.name` attribute.
127
128 .. admonition:: For Extension Developers
129
77492f8 typo
Alexis Metaireau authored
130 If you are writing a Flask extension and you want to gracefully degrade for
c360f00 @mitsuhiko Added signal documentation
authored
131 missing blinker installations, you can do so by using the
132 :class:`flask.signals.Namespace` class.
133
492ef06 @rduplain Clarify use of context-locals with signals.
rduplain authored
134 .. _signals-sending:
135
c360f00 @mitsuhiko Added signal documentation
authored
136 Sending Signals
137 ---------------
138
139 If you want to emit a signal, you can do so by calling the
140 :meth:`~blinker.base.Signal.send` method. It accepts a sender as first
141 argument and optionally some keyword arguments that are forwarded to the
142 signal subscribers::
143
144 class Model(object):
145 ...
146
147 def save(self):
148 model_saved.send(self)
149
150 Try to always pick a good sender. If you have a class that is emitting a
663802e @defuz docs: ``with``, ``for``, ``self``
defuz authored
151 signal, pass ``self`` as sender. If you are emitting a signal from a random
c360f00 @mitsuhiko Added signal documentation
authored
152 function, you can pass ``current_app._get_current_object()`` as sender.
153
154 .. admonition:: Passing Proxies as Senders
155
156 Never pass :data:`~flask.current_app` as sender to a signal. Use
157 ``current_app._get_current_object()`` instead. The reason for this is
158 that :data:`~flask.current_app` is a proxy and not the real application
159 object.
160
492ef06 @rduplain Clarify use of context-locals with signals.
rduplain authored
161
162 Signals and Flask's Request Context
163 -----------------------------------
164
f071990 @rduplain Fix reqcontext ref in signals doc.
rduplain authored
165 Signals fully support :ref:`request-context` when receiving signals.
166 Context-local variables are consistently available between
167 :data:`~flask.request_started` and :data:`~flask.request_finished`, so you can
168 rely on :class:`flask.g` and others as needed. Note the limitations described
169 in :ref:`signals-sending` and the :data:`~flask.request_tearing_down` signal.
492ef06 @rduplain Clarify use of context-locals with signals.
rduplain authored
170
171
0510867 @mitsuhiko Documented blinker 1.1 changes
authored
172 Decorator Based Signal Subscriptions
173 ------------------------------------
174
175 With Blinker 1.1 you can also easily subscribe to signals by using the new
176 :meth:`~blinker.base.NamedSignal.connect_via` decorator::
177
178 from flask import template_rendered
179
180 @template_rendered.connect_via(app)
230e136 @mitsuhiko Subscribe to signals with extra kwargs in the docs
authored
181 def when_template_rendered(sender, template, context, **extra):
0510867 @mitsuhiko Documented blinker 1.1 changes
authored
182 print 'Template %s is rendered with %s' % (template.name, context)
183
c360f00 @mitsuhiko Added signal documentation
authored
184 Core Signals
185 ------------
186
187 .. when modifying this list, also update the one in api.rst
188
189 The following signals exist in Flask:
190
191 .. data:: flask.template_rendered
192 :noindex:
193
194 This signal is sent when a template was successfully rendered. The
195 signal is invoked with the instance of the template as `template`
196 and the context as dictionary (named `context`).
197
198 Example subscriber::
199
230e136 @mitsuhiko Subscribe to signals with extra kwargs in the docs
authored
200 def log_template_renders(sender, template, context, **extra):
c360f00 @mitsuhiko Added signal documentation
authored
201 sender.logger.debug('Rendering template "%s" with context %s',
202 template.name or 'string template',
203 context)
204
1c24b62 @fsouza Minor on docs (fixes #133)
fsouza authored
205 from flask import template_rendered
206 template_rendered.connect(log_template_renders, app)
c360f00 @mitsuhiko Added signal documentation
authored
207
208 .. data:: flask.request_started
209 :noindex:
210
ffe0c54 @jeffwidman Grammer and readability fixes
jeffwidman authored
211 This signal is sent when the request context is set up, before
212 any request processing happens. Because the request context is already
c360f00 @mitsuhiko Added signal documentation
authored
213 bound, the subscriber can access the request with the standard global
214 proxies such as :class:`~flask.request`.
215
216 Example subscriber::
217
230e136 @mitsuhiko Subscribe to signals with extra kwargs in the docs
authored
218 def log_request(sender, **extra):
c360f00 @mitsuhiko Added signal documentation
authored
219 sender.logger.debug('Request context is set up')
220
221 from flask import request_started
3b0eb0f @mitsuhiko Added notes on proxies
authored
222 request_started.connect(log_request, app)
c360f00 @mitsuhiko Added signal documentation
authored
223
224 .. data:: flask.request_finished
225 :noindex:
226
227 This signal is sent right before the response is sent to the client.
228 It is passed the response to be sent named `response`.
229
230 Example subscriber::
231
230e136 @mitsuhiko Subscribe to signals with extra kwargs in the docs
authored
232 def log_response(sender, response, **extra):
c360f00 @mitsuhiko Added signal documentation
authored
233 sender.logger.debug('Request context is about to close down. '
234 'Response: %s', response)
235
236 from flask import request_finished
3b0eb0f @mitsuhiko Added notes on proxies
authored
237 request_finished.connect(log_response, app)
c360f00 @mitsuhiko Added signal documentation
authored
238
239 .. data:: flask.got_request_exception
240 :noindex:
241
242 This signal is sent when an exception happens during request processing.
243 It is sent *before* the standard exception handling kicks in and even
244 in debug mode, where no exception handling happens. The exception
245 itself is passed to the subscriber as `exception`.
246
247 Example subscriber::
248
230e136 @mitsuhiko Subscribe to signals with extra kwargs in the docs
authored
249 def log_exception(sender, exception, **extra):
c360f00 @mitsuhiko Added signal documentation
authored
250 sender.logger.debug('Got exception during processing: %s', exception)
251
252 from flask import got_request_exception
3b0eb0f @mitsuhiko Added notes on proxies
authored
253 got_request_exception.connect(log_exception, app)
c360f00 @mitsuhiko Added signal documentation
authored
254
e71a5ff @mitsuhiko Started work on new request dispatching. Unittests not yet updated
authored
255 .. data:: flask.request_tearing_down
256 :noindex:
257
258 This signal is sent when the request is tearing down. This is always
259 called, even if an exception is caused. Currently functions listening
260 to this signal are called after the regular teardown handlers, but this
261 is not something you can rely on.
262
263 Example subscriber::
264
230e136 @mitsuhiko Subscribe to signals with extra kwargs in the docs
authored
265 def close_db_connection(sender, **extra):
e71a5ff @mitsuhiko Started work on new request dispatching. Unittests not yet updated
authored
266 session.close()
267
268 from flask import request_tearing_down
269 request_tearing_down.connect(close_db_connection, app)
270
9bed20c @mitsuhiko Added documentation for appcontext and teardown handlers
authored
271 As of Flask 0.9, this will also be passed an `exc` keyword argument
272 that has a reference to the exception that caused the teardown if
273 there was one.
274
275 .. data:: flask.appcontext_tearing_down
276 :noindex:
277
278 This signal is sent when the app context is tearing down. This is always
279 called, even if an exception is caused. Currently functions listening
280 to this signal are called after the regular teardown handlers, but this
281 is not something you can rely on.
282
283 Example subscriber::
284
285 def close_db_connection(sender, **extra):
286 session.close()
287
5b462dd @mitsuhiko Fixed a typo in the docs. This fixes #576 and #575
authored
288 from flask import appcontext_tearing_down
9bed20c @mitsuhiko Added documentation for appcontext and teardown handlers
authored
289 appcontext_tearing_down.connect(close_db_connection, app)
290
291 This will also be passed an `exc` keyword argument that has a reference
292 to the exception that caused the teardown if there was one.
293
0676bb8 @mitsuhiko Added appcontext_pushed and appcontext_popped signals
authored
294 .. data:: flask.appcontext_pushed
295 :noindex:
296
297 This signal is sent when an application context is pushed. The sender
298 is the application. This is usually useful for unittests in order to
299 temporarily hook in information. For instance it can be used to
300 set a resource early onto the `g` object.
301
302 Example usage::
303
304 from contextlib import contextmanager
305 from flask import appcontext_pushed
306
307 @contextmanager
308 def user_set(app, user):
309 def handler(sender, **kwargs):
310 g.user = user
311 with appcontext_pushed.connected_to(handler, app):
312 yield
313
314 And in the testcode::
315
316 def test_user_me(self):
317 with user_set(app, 'john'):
318 c = app.test_client()
319 resp = c.get('/users/me')
320 assert resp.data == 'username=john'
321
322 .. versionadded:: 0.10
323
e59e005 @mitsuhiko Fixed a documentation typo
authored
324 .. data:: flask.appcontext_popped
a8a98bc @mitsuhiko Various documentation fixups
authored
325 :noindex:
0676bb8 @mitsuhiko Added appcontext_pushed and appcontext_popped signals
authored
326
327 This signal is sent when an application context is popped. The sender
328 is the application. This usually falls in line with the
329 :data:`appcontext_tearing_down` signal.
330
331 .. versionadded:: 0.10
332
333
0faed95 @mitsuhiko Documented new signal message_flashed
authored
334 .. data:: flask.message_flashed
335 :noindex:
336
337 This signal is sent when the application is flashing a message. The
338 messages is sent as `message` keyword argument and the category as
339 `category`.
340
341 Example subscriber::
342
343 recorded = []
344 def record(sender, message, category, **extra):
345 recorded.append((message, category))
346
347 from flask import message_flashed
348 message_flashed.connect(record, app)
349
350 .. versionadded:: 0.10
351
34871a2 @DasIch Switch pypi links to https
DasIch authored
352 .. _blinker: https://pypi.python.org/pypi/blinker
Something went wrong with that request. Please try again.