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

Uploaded Python 3

File details

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

File metadata

  • Download URL: canary_framework-0.4.5.tar.gz
  • Upload date:
  • Size: 75.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.5.tar.gz
Algorithm Hash digest
SHA256 12c36d3f8e784439f13430a8094d67a63ed5f4aeee9eee58ed0ee94902741183
MD5 ee884b04d6a93aa4eb0fdcaf4c39281b
BLAKE2b-256 52ce1c6fdbbdd376e0958320cc3551a583d3727307693458a7d7df8e72d54fc0

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for canary_framework-0.4.5-py3-none-any.whl
Algorithm Hash digest
SHA256 befe0d8c4f7cc7a06b0c4b81bc05b1adfc14f2cb42a922ae318fa081d34f0f02
MD5 508cc1f4ad9643f67a77bbe32fe1edf6
BLAKE2b-256 42e0d5870c4c1190018339dbff9a3d45859f5deab71c52e5551e3841873fbf17

See more details on using hashes here.

Provenance

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