aiosignal
A lightweight async callback registry that turns a mutable list of coroutine receivers into a frozen, awaitable dispatch signal.
Repository Health
Technical Analysis
aiosignal provides a minimal, type-safe way to manage ordered lists of asynchronous callbacks in Python. Built as a thin subclass of frozenlist’s FrozenList, a Signal starts out mutable so callbacks can be registered with familiar list operations like append, then gets frozen to lock the registration phase closed before any callback fires. Once frozen, calling send() awaits every registered callback in order, giving library authors a small, well-tested primitive for exposing extension points without building their own event-dispatch machinery.
The library originated inside aiohttp, where it powers the web application’s on_startup, on_shutdown, and other lifecycle signals, and it has since become a standalone, widely depended-on part of the aio-libs ecosystem. Its entire implementation is a single module of roughly sixty lines with no runtime behavior beyond freeze/append/send, making it easy to audit and safe to pull in as a transitive dependency.
What You Get
- Signal class - A FrozenList subclass that holds an ordered list of async callback references tied to an owner object.
- Two-phase lifecycle - Callbacks are registered freely via standard list methods, then locked with freeze() so dispatch-time state can’t be mutated mid-flight.
- Async dispatch via send() - Awaits every registered callback in registration order, passing the same positional and keyword arguments through to each.
- Decorator registration - Signal instances are callable, so
@signalcan register a coroutine function directly as a receiver. - Full static typing - Ships a py.typed marker and uses PEP 646 variadic generics (TypeVarTuple) so callback argument types are checked end-to-end.
Common Use Cases
- Framework lifecycle hooks - Web frameworks like aiohttp use Signal to implement on_startup, on_cleanup, and on_shutdown events that plugins can hook into.
- Plugin/extension systems - Libraries expose a Signal as a public extension point so third-party packages can subscribe callbacks without modifying core code.
- Decoupled event notification - Application code fires a frozen signal to notify multiple independent async listeners about a state change, in place of a hand-rolled callback list.
- Middleware-style processing chains - Ordered async callbacks registered on a signal run in sequence, useful for request/response pipeline steps.
Under The Hood
Architecture
aiosignal has almost no architecture to speak of by design: its entire public surface is the Signal class in aiosignal/__init__.py, which subclasses frozenlist.FrozenList and adds an _owner slot, an async send() method, and a __call__ decorator helper. There is no internal layering, no module boundary to cross, and no hidden state beyond the inherited frozen-list buffer and the owner reference stored for __repr__ output. The only meaningful control flow is the freeze/append/send lifecycle enforced by the parent FrozenList (which raises on mutation once frozen) and by send()’s own explicit RuntimeError guard when called on a non-frozen signal. Because the class owns no resources and holds no additional collaborators, changing this one abstraction is a breaking change for every downstream consumer (aiohttp foremost) rather than an internal refactor risk.
Tech Stack
The package targets Python 3.10+ and depends on exactly one runtime library, frozenlist>=1.1.0, plus a conditional typing-extensions>=4.4 backport for TypeVarTuple/Unpack on Python versions below 3.11/3.13. It builds with a plain setuptools>=84 backend declared in pyproject.toml, with metadata (including version = attr: aiosignal.__version__) still living in setup.cfg. There are no web, database, or CLI frameworks involved — this is a pure language-level utility with no I/O and no external service integrations.
Code Quality
Tests live in tests/test_signals.py and run under pytest with pytest-asyncio-style coroutine tests, covering positional/keyword dispatch, decorator registration, frozen-state mutation guards (append/setitem/delitem all raise RuntimeError once frozen), non-coroutine callback handling, and __repr__ formatting — a comprehensive suite relative to the module’s small surface area, run across the full supported Python matrix via tox. Type checking is strict: .mypy.ini enables disallow_untyped_defs, disallow_any_generics, warn_return_any, strict_equality, and several extra error codes, and the package ships a py.typed marker. CI (.github/workflows/ci-cd.yml) runs the test matrix plus a CodeQL security scan, and dependabot keeps dependencies current with an auto-merge workflow.
What Makes It Unique aiosignal doesn’t aim to be novel — it deliberately does one narrow thing (an ordered, freeze-then-dispatch list of async callbacks) and does it as a thin, auditable wrapper over an existing data structure rather than building a general event-bus or pub/sub system with topics, priorities, or error isolation. That restraint is its value: because it started as an internal implementation detail extracted from aiohttp’s own signal system, its API is shaped by a real, battle-tested framework use case, and its near-zero dependency footprint and readable single-file implementation make it a safe, low-risk transitive dependency for any async Python project that needs a similar extension-point primitive.
Used by 7 apps in this directory
Agno
Devops · AI Development · Automation
Build, run, and manage agent platforms with a full production stack — SDK, runtime, and control plane included.
GPT Researcher
Productivity · AI Assistants
The pioneering open-source autonomous AI agent that conducts deep, multi-source research and produces citation-backed reports exceeding 2,000 words — faster and more reliably than any human researcher.
Helicone
Monitoring · AI Development · Analytics
An open-source AI gateway and LLM observability platform that routes requests to 100+ models while logging cost, latency, and full traces for every call.
knowhere
AI Development · Developer Tools
Transform messy, unstructured documents into persistent, navigable memory that AI agents can actually use.
marimo
Developer Tools · Data Engineering
A reactive Python notebook that eliminates hidden state, runs reproducibly, and deploys as a web app or script — stored as pure Python, built for the AI era.
PostgresML
Databases · AI Development
Run ML training and LLM inference natively inside PostgreSQL with GPU acceleration — no data movement required.
SWIRL
Search · Databases · Data Engineering
Federated AI search and RAG across 100+ enterprise sources—no data extraction, no vector database required.