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.8.tar.gz (84.7 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.8-py3-none-any.whl (40.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: canary_framework-0.4.8.tar.gz
  • Upload date:
  • Size: 84.7 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.8.tar.gz
Algorithm Hash digest
SHA256 09c8eb2b514dce43e43b2d253fae48fa89d10e98be1183c29c813d6dbf789ae1
MD5 b9d89fe905a85db3a2b16517604ff87c
BLAKE2b-256 f7699a1ef8a3771834c25c3c347516da11d2ecd914f32f2b7fc4e173d0759f5c

See more details on using hashes here.

Provenance

The following attestation bundles were made for canary_framework-0.4.8.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.8-py3-none-any.whl.

File metadata

File hashes

Hashes for canary_framework-0.4.8-py3-none-any.whl
Algorithm Hash digest
SHA256 a9e45afdc1751560cb9df2a50a7418e421d08ce6a9bf72e04d0ff7e8b5922e4a
MD5 9eeb395748d9d13c0a0cc7b365c3d51c
BLAKE2b-256 8fa4e04a256ecd4de1e5d066327b5cb39fab74b82ad19808a2b557b62e421caf

See more details on using hashes here.

Provenance

The following attestation bundles were made for canary_framework-0.4.8-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