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

Uploaded Python 3

File details

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

File metadata

  • Download URL: canary_framework-0.4.7.tar.gz
  • Upload date:
  • Size: 77.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.7.tar.gz
Algorithm Hash digest
SHA256 7174e9ac713fff48e0b8c427f729b7dfbd6e51e764ebc2bb8bf4d6bce5873df1
MD5 065c2316c66db91433278d405590aa5a
BLAKE2b-256 cb6eab47ad3fd2cfdc796df9cc890f6254dcfb294617ea8d75fca161d117d3f8

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for canary_framework-0.4.7-py3-none-any.whl
Algorithm Hash digest
SHA256 8380c18bfbcb07fb80ba50026a2c2ce0fb5caff09980baf0a48c4ac646954f1b
MD5 728d6e5beb5f9957f1a049f08d7e748f
BLAKE2b-256 066bf758578126deb82bf1033a27f82f71ac8a0551b1d460b7b3a9d338a3045d

See more details on using hashes here.

Provenance

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