Signals¶
Signals are a lightweight way to notify subscribers of certain events during thelifecycle of the application and each request. When an event occurs, it emits thesignal, which calls each subscriber.
Signals are implemented by theBlinker library. See its documentation for detailedinformation. Flask provides some built-in signals. Extensions may provide their own.
Many signals mirror Flask’s decorator-based callbacks with similar names. For example,therequest_started signal is similar to thebefore_request()decorator. The advantage of signals over handlers is that they can be subscribed totemporarily, and can’t directly affect the application. This is useful for testing,metrics, auditing, and more. For example, if you want to know what templates wererendered at what parts of what requests, there is a signal that will notify you of thatinformation.
Core Signals¶
SeeSignals for a list of all built-in signals. TheApplication Structure and Lifecyclepage also describes the order that signals and decorators execute.
Subscribing to Signals¶
To subscribe to a signal, you can use theconnect() method of a signal. The firstargument is the function that should be called when the signal is emitted,the optional second argument specifies a sender. To unsubscribe from asignal, you can use thedisconnect() method.
For all core Flask signals, the sender is the application that issued thesignal. When you subscribe to a signal, be sure to also provide a senderunless you really want to listen for signals from all applications. This isespecially true if you are developing an extension.
For example, here is a helper context manager that can be used in a unit testto determine which templates were rendered and what variables were passedto the template:
fromflaskimporttemplate_renderedfromcontextlibimportcontextmanager@contextmanagerdefcaptured_templates(app):recorded=[]defrecord(sender,template,context,**extra):recorded.append((template,context))template_rendered.connect(record,app)try:yieldrecordedfinally:template_rendered.disconnect(record,app)
This can now easily be paired with a test client:
withcaptured_templates(app)astemplates:rv=app.test_client().get('/')assertrv.status_code==200assertlen(templates)==1template,context=templates[0]asserttemplate.name=='index.html'assertlen(context['items'])==10
Make sure to subscribe with an extra**extra argument so that yourcalls don’t fail if Flask introduces new arguments to the signals.
All the template rendering in the code issued by the applicationappin the body of thewith block will now be recorded in thetemplatesvariable. Whenever a template is rendered, the template object as well ascontext are appended to it.
Additionally there is a convenient helper method(connected_to()) that allows you totemporarily subscribe a function to a signal with a context manager onits own. Because the return value of the context manager cannot bespecified that way, you have to pass the list in as an argument:
fromflaskimporttemplate_rendereddefcaptured_templates(app,recorded,**extra):defrecord(sender,template,context):recorded.append((template,context))returntemplate_rendered.connected_to(record,app)
The example above would then look like this:
templates=[]withcaptured_templates(app,templates,**extra):...template,context=templates[0]
Creating Signals¶
If you want to use signals in your own application, you can use theblinker library directly. The most common use case are named signals in acustomNamespace. This is what is recommendedmost of the time:
fromblinkerimportNamespacemy_signals=Namespace()
Now you can create new signals like this:
model_saved=my_signals.signal('model-saved')
The name for the signal here makes it unique and also simplifiesdebugging. You can access the name of the signal with thename attribute.
Sending Signals¶
If you want to emit a signal, you can do so by calling thesend() method. It accepts a sender as firstargument and optionally some keyword arguments that are forwarded to thesignal subscribers:
classModel(object):...defsave(self):model_saved.send(self)
Try to always pick a good sender. If you have a class that is emitting asignal, passself as sender. If you are emitting a signal from a randomfunction, you can passcurrent_app._get_current_object() as sender.
Passing Proxies as Senders
Never passcurrent_app as sender to a signal. Usecurrent_app._get_current_object() instead. The reason for this isthatcurrent_app is a proxy and not the real applicationobject.
Signals and Flask’s Request Context¶
Signals fully supportThe Request Context when receiving signals.Context-local variables are consistently available betweenrequest_started andrequest_finished, so you canrely onflask.g and others as needed. Note the limitations describedinSending Signals and therequest_tearing_down signal.
Decorator Based Signal Subscriptions¶
You can also easily subscribe to signals by using theconnect_via() decorator:
fromflaskimporttemplate_rendered@template_rendered.connect_via(app)defwhen_template_rendered(sender,template,context,**extra):print(f'Template{template.name} is rendered with{context}')