Blinker
Fast Python in-process signal/event dispatching for decoupled object-to-object notifications
Repository Health
Technical Analysis
Blinker provides a fast dispatching system that lets any number of interested parties subscribe to “signals” — named or anonymous notification channels that decouple the code emitting an event from the code reacting to it. A signal can be sent by any sender, and receivers can subscribe either to a specific sender or to any sender via the ANY symbol, making it a lightweight alternative to building a bespoke observer/pub-sub layer.
It’s best known as the signal system underneath Flask (flask.signals) and is used across the Pallets ecosystem and beyond wherever code needs to broadcast “something happened” without hard-wiring callers to callbacks. Receivers are tracked with weak references by default so they don’t leak memory when the subscribing object is garbage collected, and the library supports both sync and async (send_async) dispatch.
What You Get
SignalandNamedSignalclasses with.connect(),.connect_via()(decorator form), and.disconnect()- Sender-scoped subscriptions plus the
ANYsymbol for sender-agnostic receivers - Weak-reference tracking by default so receivers don’t need manual cleanup as objects are garbage collected
- Built-in
receiver_connected/receiver_disconnectedmeta-signals for observing subscription changes connected_to()andmuted()context managers for scoping connections and temporarily silencing a signal (handy in tests)- Both synchronous
send()and asynchronoussend_async()dispatch, including mixed sync/async receiver support via wrapper callbacks
Common Use Cases
- Implementing plugin/extension hook points that other code can subscribe to without the emitter knowing about them
- Decoupling application lifecycle events (e.g. “user created”, “request finished”) from the modules that react to them
- Powering framework-level signals, as Flask does internally for request/response and app lifecycle events
- Writing testable event-driven code where tests can temporarily mute or intercept signals via
connected_to()/muted()
Under The Hood
Architecture - The entire implementation lives in src/blinker/base.py. Signal maintains three internal maps — receivers (id to weakref-or-strong-ref callable), _by_sender and _by_receiver (bidirectional id sets for fast lookup) — plus a _weak_senders map so senders themselves can be weakly tracked and trigger cleanup when garbage collected. NamedSignal adds a name attribute and is the type returned by the module-level signal() function, which is bound to a shared default_namespace (a Namespace dict subclass) so repeated calls with the same name return the same signal instance. receivers_for() is a generator that resolves weakrefs lazily at send time and prunes dead ones inline, avoiding upfront iteration cost for large receiver sets.
Tech Stack - Pure Python 3.9+ standard library only (weakref, collections, contextlib, inspect.iscoroutinefunction), packaged with Flit. Dev tooling uses uv/tox for environment management, ruff for linting, and both mypy (strict mode) and pyright for type checking — the package ships a py.typed marker.
Code Quality - Test coverage is organized into tests/test_signals.py, tests/test_context.py, and tests/test_symbol.py, exercising connect/disconnect lifecycles, weak-reference garbage collection behavior, sender-scoped vs ANY dispatch, and both sync and async send paths (via pytest-asyncio). mypy --strict plus pyright --verifytypes are both wired into tox, and pytest.ini_options turns warnings into errors, indicating a low tolerance for silent deprecation drift. The _cleanup_bookkeeping() method is explicitly documented as not thread-safe with a stated rationale, which is a good sign of intentional, documented trade-offs rather than accidental gaps.
API Design - The core workflow is two calls: signal('name') to get or create a NamedSignal, then .connect(receiver) and .send(sender). The connect_via() decorator and connected_to() context manager cover the two most common ergonomic needs (declarative subscription, scoped test subscription) without extra ceremony, and defaulting weak=True means most consumers get correct memory behavior without thinking about it, at the cost of a documented gotcha for receivers defined as local closures.
Used by 6 apps in this directory
Apache Airflow
Data Engineering
Define, schedule, and monitor complex data workflows as Python code — with a powerful UI, 80+ provider integrations, and battle-tested scalability across thousands of production deployments.
changedetection.io
Monitoring
Self-hosted website change detection with AI-powered smart alerts, browser automation, price tracking, and 85+ notification channels.
ClickHouse
Databases · Analytics · Data Engineering
Open-source column-oriented database that delivers real-time analytical queries on petabyte-scale data with millisecond latency.
highlight.io
Developer Tools · Analytics · Monitoring
Open-source full-stack monitoring that unifies session replay, error tracking, logging, and distributed tracing so you can stop context-switching between tools.
Redash
Analytics · Data Engineering
Redash lets anyone connect to 35+ SQL and NoSQL data sources, write a query in the browser, and turn the result into a shared dashboard — no separate BI suite required.
rowboat
AI Assistants · AI Development
Build, test, and deploy multi-agent AI workflows with a visual editor, RAG data sources, MCP tool integration, and a production-ready REST API.