Signals (Blinker)
Signals are a publish/subscribe mechanism: one part of your app broadcasts a named event and any number of connected receiver functions run in response, with neither knowing about the other.
Learn Signals (Blinker) in our free Flask course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Flask course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll connect receivers to Flask's built-in signals, define your own with Blinker, and use signals to decouple side effects from your core logic.
Flask emits signals at key moments — for example request_started fires at the start of every request. A receiver is a function you connect to a signal; Flask calls it whenever the signal is sent . The sender doesn't know who is listening, which keeps things loosely coupled.
The runnable example below — using real Flask signals — connects a receiver to request_started and records each request. Both a normal request and a 404 trigger it, because the signal fires before routing decides the outcome.
Both requests are recorded — including the 404 — because request_started notifies you the moment a request begins, independent of how it ends.
Signals carry data . The template_rendered signal fires after Jinja renders a template and passes the template and the context (the variables it received). This is invaluable for debugging and testing — you can assert which template ran and what data it was given.
The runnable example connects a receiver that records the template name and the context keys whenever a page renders.
The receiver captured one render with name in its context — exactly the pattern Flask's own test helpers use to assert which template a view rendered.
The real power is your own signals. With Blinker (shipped with Flask) you create a named signal, connect any number of receivers, and broadcast with send . This decouples side effects: your registration code just announces "a user registered" and walks away — auditing, email, and analytics each subscribe on their own.
The runnable example fires one user-registered signal and watches two independent receivers — an audit logger and a welcome-emailer — both react, without the core register_user function knowing they exist.
Complete the code below. Replace each ___ to define a signal, connect a receiver, and broadcast it.
Either you never connect ed it, or you connected to a different signal instance. Use the same named signal("x") everywhere — Blinker returns the same object for the same name.
❌ Receiver is garbage-collected and stops firing
Blinker holds receivers with weak references by default, so a local function can be collected. Keep a reference, or use connect(fn, weak=False) to keep it alive.
Build a custom signal with two receivers that track page views differently.
Lesson complete — your app components can talk without coupling!
You connected receivers to request_started and template_rendered , created custom signals with Blinker, fanned one event out to many receivers, and decoupled side effects from your core logic.
🚀 Up next: Async Views & ASGI — run async def views for concurrent I/O.
Practice quiz
What pattern do Flask signals implement?
- Request/response
- Map/reduce
- Publish/subscribe
- Master/worker
Answer: Publish/subscribe. Signals are publish/subscribe: a sender broadcasts and receivers react.
Which library powers Flask's signals?
- Celery
- Redis
- Werkzeug
- Blinker
Answer: Blinker. Flask's signals are built on the Blinker library, shipped with Flask.
What is a function that runs when a signal is sent called?
- A receiver
- A sender
- A middleware
- A blueprint
Answer: A receiver. A receiver is connected to a signal and runs when it is sent.
Which built-in signal fires at the start of every request?
- template_rendered
- request_started
- got_request_exception
- request_finished
Answer: request_started. request_started fires when a request begins, before routing decides the outcome.
What does the template_rendered signal pass to its receivers?
- The status code
- The session
- The template and the context
- The request body
Answer: The template and the context. template_rendered passes the rendered template and its context dict.
How do you create a custom named signal with Blinker?
- Signal.new("name")
- blinker.create("name")
- event("name")
- signal("name")
Answer: signal("name"). signal("name") returns the signal object (same instance for the same name).
How do you broadcast a signal to its receivers?
- sig.send(sender, **data)
- sig.emit(data)
- sig.fire()
- sig.publish()
Answer: sig.send(sender, **data). sig.send(sender, **data) notifies all connected receivers.
Which built-in signal fires when an unhandled error occurs?
- request_started
- got_request_exception
- template_rendered
- request_finished
Answer: got_request_exception. got_request_exception fires when an unhandled exception is raised.
How are signals different from a before_request hook?
- Signals run only once
- Signals modify the response
- Signals are decoupled notifications that can't change the outcome
- Signals replace routing
Answer: Signals are decoupled notifications that can't change the outcome. Hooks process the request and can short-circuit it; signals are decoupled side-effect notifications.
Why might a Blinker receiver stop firing unexpectedly?
- Signals expire after one send
- It must be async
- Flask disables signals in debug
- It was garbage-collected due to weak references
Answer: It was garbage-collected due to weak references. Blinker holds receivers weakly by default; keep a reference or use weak=False.