Canary Framework
A minimal, decorator-driven framework for dependency injection, lifecycle, and ASGI web apps — in plain Python.
中文 · Documentation · Changelog
Install
pip install canary-framework # core
pip install "canary-framework[web]" # + web extension (ASGI / OpenAPI)
Requires Python 3.12+.
The model
- cocoa is the smallest runnable unit — a plain Python class marked with
@cocoa. Dependencies are declared withdeps=[...];@on_init/@on_start/@on_stopdeclare optional lifecycle behaviour. - Canary is the orchestrator.
Canary(*roots)resolves the dependency graph, topologically sorts it, and drives the full lifecycle — and is itself an ASGI application.
Quick start
import asyncio
from canary_framework import Canary, cocoa, on_start
@cocoa
class Config:
def __init__(self) -> None:
self.database_url = "postgresql://localhost/dev"
@cocoa(deps=[Config])
class Database:
@on_start
async def connect(self) -> None:
print(f"connecting to {self.config.database_url}") # self.config is injected
@cocoa(deps=[Database])
class UserService: ...
async def main() -> None:
app = Canary(UserService)
await app.init() # build the graph, run @on_init
await app.start() # inject deps, run @on_start
assert app[Database].config is app[Config]
await app.stop() # run @on_stop in reverse order
asyncio.run(main())
Dependency injection
Cocoas declare dependencies with deps=[...] — no __init__ plumbing, no DSL. Each dependency
is injected lazily as self.<snake_case_name> at start():
@cocoa(deps=[Database, Cache])
class UserService:
def __init__(self) -> None:
self._ready = False # no dependency wiring here
Canary resolves the graph from the roots, injects one shared instance per type, and drives
initialization and startup in topological order.
Lifecycle
Three optional hooks — each sync or async, any number per stage:
| Stage | Decorator | Runs |
|---|---|---|
| Init | @on_init |
init(), topological order |
| Start | @on_start |
start(), topological order, deps injected |
| Stop | @on_stop |
stop(), reverse topological order |
@cocoa(deps=[Config])
class Database:
@on_init
def build_pool(self) -> None: ...
@on_start
async def connect(self) -> None: ...
@on_stop
async def disconnect(self) -> None: ...
Web apps
The web extension turns a @cocoa service into a FastAPI-style ASGI app with automatic
OpenAPI docs:
from pydantic import BaseModel
from canary_framework import Canary
from canary_framework.web import get, post, web_cocoa
class BorrowRequest(BaseModel):
member_id: int
@web_cocoa(deps=[BookRepository, LibraryService])
class LibraryAPI:
@get("/books/{book_id}")
async def get_book(self, book_id: int) -> dict: ...
@post("/books/{book_id}/borrow")
async def borrow(self, book_id: int, body: BorrowRequest) -> dict: ...
app = Canary(LibraryAPI) # `app` is the ASGI application
uvicorn examples.library.web:app --reload
# GET /docs · /redoc · /openapi.json
Examples
Runnable examples live in examples/, from a minimal unit through dependency
injection, lifecycle hooks, multi-root composition, and a layered library web app.
Documentation
- Quick Start
- Cocoa Units · Runtime (Canary)
- Lifecycle · Dependency Injection
- Web Apps · Architecture · API Reference
License
Apache-2.0.
Release files for canary-framework 0.9.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| canary_framework-0.9.1.tar.gz | 68.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| canary_framework-0.9.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 104.7 kB
Release files / canary_framework-0.9.1.tar.gz
| Download URL | canary_framework-0.9.1.tar.gz |
|---|---|
| Size | 68.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
528718ea37b7f6a9e230e9e061163c7b36bdeb4dbe4eaa541e4fa78fa9af9e24
|
|
BLAKE2b-256 checksum How to use checksums |
d7a7a9ea8e2e62d2994a2408ebc305445c6f3de0dc01780db775f825a3226c01
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.
Transparency logRelease files / canary_framework-0.9.1-py3-none-any.whl
| Download URL | canary_framework-0.9.1-py3-none-any.whl |
|---|---|
| Size | 36.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6bbb7693bc9da2b8ace1f54fb1041fed61cdd2467e827ddb5cabf2b6dce4c843
|
|
BLAKE2b-256 checksum How to use checksums |
641357739e1acf1a5330e9097952a8bfa25d4310f1ce65376c6ada8cb85dbaf7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.
Transparency log