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.