Flask Signals

Signals allow different parts of a Flask application to communicate without tight coupling. When something important happens — a request starts, a user logs in, a template renders — Flask sends a signal. Any part of your code can subscribe to that signal and run a function in response.

The Event Notification Analogy

Think of signals like a newsletter. The newsletter publisher (Flask) sends an issue when something happens. Subscribers (your listener functions) receive it and react however they want. The publisher does not know or care who subscribes — it just sends the signal.

Flask sends:   request_started signal
                      │
         ┌────────────┼────────────┐
         ▼            ▼            ▼
   Log the request  Track analytics  Check rate limit
   (listener 1)     (listener 2)     (listener 3)

Flask's Built-In Signals

Flask uses the blinker library for signals. Install it first:

pip install blinker
SignalFired When
request_startedBefore every request is dispatched
request_finishedAfter a request is handled (before response sent)
request_tearing_downAfter request teardown
got_request_exceptionWhen an unhandled exception occurs
template_renderedAfter a template is rendered
appcontext_pushedWhen an app context is pushed

Subscribing to a Signal

from flask import request
from flask.signals import request_started, got_request_exception

def log_request(sender, **kwargs):
    print(f'[REQUEST] {request.method} {request.path}')

def log_exception(sender, exception, **kwargs):
    print(f'[ERROR] Unhandled exception: {exception}')

# Connect listeners
request_started.connect(log_request, app)
got_request_exception.connect(log_exception, app)

The sender argument is the Flask app that fired the signal. Always pass app as the second argument to .connect() so your listener fires only for your app (not for all Flask apps in the process).

Creating Custom Signals

Define your own signals for application-level events:

from blinker import Namespace

my_signals = Namespace()

user_registered = my_signals.signal('user-registered')
order_placed    = my_signals.signal('order-placed')
payment_failed  = my_signals.signal('payment-failed')

Sending a Custom Signal

@app.route('/register', methods=['POST'])
def register():
    user = create_new_user(request.form)
    db.session.commit()
    # Send signal with the new user as extra data
    user_registered.send(app, user=user)
    return redirect(url_for('login'))

Listening to a Custom Signal

@user_registered.connect_via(app)
def send_welcome_email(sender, user, **kwargs):
    # Send a welcome email to the new user
    send_email(to=user.email, subject='Welcome!', body='Thanks for joining.')

@user_registered.connect_via(app)
def assign_default_role(sender, user, **kwargs):
    user.role = 'member'
    db.session.commit()

@user_registered.connect_via(app)
def log_registration(sender, user, **kwargs):
    app.logger.info('New user registered: %s', user.email)

Three listeners subscribe to the same signal. All three fire when a user registers. The registration route does not need to know about emails, roles, or logging — it just sends the signal.

Signals vs Before/After Request Hooks

FeatureRequest HooksSignals
ScopeEvery requestSpecific events you define
Multiple subscribersOne function per hookUnlimited subscribers
DecouplingModerateHigh
Return valueCan return a responseCannot return a response

Summary

Flask signals let your application broadcast events without the broadcaster knowing who listens. Use built-in signals to hook into Flask's lifecycle (request start, exceptions, template rendering). Create custom signals for domain events like user registration, order placement, or payment success. Multiple independent listeners can subscribe to the same signal, keeping business logic decoupled and easy to extend without modifying existing code.

Leave a Comment

Your email address will not be published. Required fields are marked *