Skip to main content

Lightweight decorator-driven Python async service framework with dependency injection

Project description

Canary Framework

Lightweight Python Async Service Framework — Decorator-Driven, Zero Boilerplate

License Python CI GitHub Stars


Canary Framework is a decorator-driven async service framework for Python. Core philosophy: Services are the smallest unit, modules compose services, and modules themselves are services.

Core Features

  • Decorator-Driven — Use @service, @module, @router decorators, zero inheritance required
  • Topological Startup — Kahn's algorithm ensures dependencies start first
  • Dependency Injectiondeps=[DBService] auto-injected as self.db_service
  • Lifecycle Management@after_config/@after_init/@before_startup/@before_shutdown hooks
  • ASGI Compatible — Built on Starlette, works with uvicorn and other ASGI servers
  • Modular Architecture — Hierarchical composition with nested modules
  • OpenAPI Support — Auto-generated Swagger UI and ReDoc documentation

Design Principles

  1. Decorator-Driven — Code is configuration, no complex setup required
  2. Async-First — Built on async/await for high performance
  3. Explicit Dependencies — Clear dependency declarations for better understanding and testing
  4. Convention Over Configuration — Auto-injection, auto-mounting
  5. Composability — Build complex systems through module composition

Installation

pip install canary-framework

Quick Start

from canary_framework import module, service, router, get, post, after_config

@service(name="database")
class DatabaseService:
    def __init__(self):
        self.connection = None
    
    @after_config
    async def connect(self):
        self.connection = "connected"
        print("Database connected")

@service(name="user_service", deps=[DatabaseService])
class UserService:
    async def get_user(self, user_id):
        return {"id": user_id, "name": "User"}

@router(name="api", prefix="/api", deps=[UserService])
class ApiRouter:
    @get("/users/{user_id}")
    async def get_user(self, request):
        user_id = request.path_params["user_id"]
        return await self.user_service.get_user(int(user_id))
    
    @post("/users")
    async def create_user(self, request):
        data = await request.json()
        return {"id": 1, **data}, 201

@module(name="app", services=[DatabaseService, UserService, ApiRouter])
class AppModule:
    pass

# Run with uvicorn
# uvicorn main:AppModule --host 0.0.0.0 --port 8000 --reload

Web Example with OpenAPI

from canary_framework import module, router, get, post
from pydantic import BaseModel, Field

class UserRequest(BaseModel):
    name: str = Field(description="User name")
    email: str = Field(description="User email")

class UserResponse(BaseModel):
    id: int = Field(description="User ID")
    name: str = Field(description="User name")
    email: str = Field(description="User email")

@router(name="users", prefix="/users", tags=["Users"])
class UsersRouter:
    @get("/", summary="List users", description="Get all users")
    async def list_users(self, request):
        return {"users": []}
    
    @post("/", 
          summary="Create user", 
          description="Create a new user",
          request_model=UserRequest,
          response_model=UserResponse)
    async def create_user(self, request, user: UserRequest):
        return {"id": 1, **user.model_dump()}, 201

@module(name="app", services=[UsersRouter])
class AppModule:
    pass

OpenAPI Documentation

Access automatically generated documentation:

  • Swagger UI: http://localhost:8000/docs
  • ReDoc: http://localhost:8000/redoc
  • OpenAPI JSON: http://localhost:8000/openapi.json

Documentation

📖 Complete documentation: Canary Framework Docs

Documentation Structure

  • Quickstart — Build a complete application from scratch
  • Services — Service definition, dependency injection, lifecycle
  • Modules — Module composition, hierarchical structure
  • Web Routing — Routing, HTTP methods, request handling
  • Dependency Injection — DI system, topological sorting, registry
  • Lifecycle — Lifecycle hooks, best practices
  • Core Concepts — Design principles, architecture, internals
  • API Reference — Complete API documentation

Architecture

src/canary_framework/
├── common/              # Shared types, enums, exceptions
│   ├── errors.py        # Framework exceptions
│   ├── markers.py       # Metadata markers and accessors
│   └── types.py         # Data classes and type aliases
├── core/                # Core base classes
│   ├── module.py        # ModuleBase - module orchestration
│   ├── service.py       # ServiceBase - lifecycle management
│   └── router.py        # RouterBase - ASGI routing
├── decorators/         # Decorator implementations
│   ├── module.py        # @module decorator
│   ├── service.py       # @service decorator
│   ├── router.py        # @router, @get/@post/... decorators
│   └── lifecycle.py     # @after_config, @after_init, etc.
└── engine/             # Core engine components
    ├── registry.py      # Registry - service registration
    ├── injector.py      # Dependency injection, topological sort
    ├── hooks.py         # Lifecycle hook discovery
    ├── openapi.py       # OpenAPI schema generation
    ├── utils.py         # Helper utilities
    └── logging.py       # Logging utilities

Lifecycle Flow

AppModule.configure()
  ├── Collect all services
  ├── Build dependency graph
  ├── Topological sort (Kahn's algorithm)
  ├── Instantiate services
  ├── Inject dependencies
  └── Call configure() + @after_config hooks on each service

AppModule.init()
  └── Call init() + @after_init hooks on each service

AppModule.startup()
  └── Call startup() + @before_startup hooks on each service

AppModule.shutdown()
  └── Call shutdown() + @before_shutdown hooks in reverse order

Testing

# Run all tests
pytest

# Run unit tests
pytest tests/unit/

# Run integration tests
pytest tests/integration/

Community

Contributing

See CONTRIBUTING.md.

License

Apache 2.0 · Copyright 2026 Zhang Wenbo (Canary)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

canary_framework-0.4.1.tar.gz (72.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

canary_framework-0.4.1-py3-none-any.whl (37.0 kB view details)

Uploaded Python 3

File details

Details for the file canary_framework-0.4.1.tar.gz.

File metadata

  • Download URL: canary_framework-0.4.1.tar.gz
  • Upload date:
  • Size: 72.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for canary_framework-0.4.1.tar.gz
Algorithm Hash digest
SHA256 e0de92870340002b8ae5b0e1944b951379da57629e2a16d8a47a3b356fe813ac
MD5 5d20e9ceca28e6fcca0bf042d19af73a
BLAKE2b-256 bfba028ce99073ff842ec3200a2e7ab4761b65ea7fc880c7d7c002110d04d780

See more details on using hashes here.

Provenance

The following attestation bundles were made for canary_framework-0.4.1.tar.gz:

Publisher: publish.yml on HotcocoaCanary/Canary-Framework

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file canary_framework-0.4.1-py3-none-any.whl.

File metadata

File hashes

Hashes for canary_framework-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b3f30d19ddcbc4c0da9c9e579486531979892229bb74c7767fa4dd98f494f2a0
MD5 949c1fca8fb2165fc93237293f3f6a75
BLAKE2b-256 3f0df672b933953f517834c5d990a464826482f70d7c5c713d86307d6ed56e36

See more details on using hashes here.

Provenance

The following attestation bundles were made for canary_framework-0.4.1-py3-none-any.whl:

Publisher: publish.yml on HotcocoaCanary/Canary-Framework

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page