Skip to main content

A modern, type-safe Dependency Injection framework for Python with support for async/await and context managers.

Project description

spritze

CI Code Quality PyPI version License: Apache 2.0

A modern, type-safe Dependency Injection framework for Python with support for async/await and context managers.

Features

  • Type Safety: Full support for Python 3.11+ type hints with Annotated and Depends
  • Scopes: APP (singleton) and REQUEST (per-request) scoping
  • Async Support: Native async/await support for providers and injection
  • Context Managers: Automatic lifecycle management with generators and async generators
  • Framework Agnostic: Easy integration with Flask, FastAPI, Litestar, Django, and more
  • No Globals: Explicit containers with per-request context management
  • Clean Architecture: Follows Domain-Driven Design principles

Installation

uv add spritze
# or
pip install spritze

Quick Start

Basic Usage

from typing import Annotated
from spritze import Container, Scope, provider, Depends, init, inject


class DatabaseConfig:
    def __init__(self, url: str) -> None:
        self.url: str = url


class DatabaseConnection:
    def __init__(self, config: DatabaseConfig) -> None:
        self.config: DatabaseConfig = config
    
    def query(self, sql: str) -> str:
        return f"Executed: {sql} on {self.config.url}"


class UserService:
    def __init__(self, db: DatabaseConnection) -> None:
        self.db: DatabaseConnection = db
    
    def get_user(self, user_id: int) -> str:
        return self.db.query(f"SELECT * FROM users WHERE id = {user_id}")


class AppContainer(Container):
    @provider(scope=Scope.APP)
    def config(self) -> DatabaseConfig:
        return DatabaseConfig("postgresql://localhost/db")
    
    @provider(scope=Scope.REQUEST)
    def database(self, config: DatabaseConfig) -> DatabaseConnection:
        return DatabaseConnection(config)
    
    @provider(scope=Scope.REQUEST)
    def user_service(self, db: DatabaseConnection) -> UserService:
        return UserService(db)


# Initialize container
container = AppContainer()
init(container)


@inject
def get_user_handler(
    user_id: int,
    service: Annotated[UserService, Depends()]
) -> str:
    return service.get_user(user_id)


if __name__ == "__main__":
    result = get_user_handler(123)
    print(result)  # -> Executed: SELECT * FROM users WHERE id = 123 on postgresql://localhost/db

Async Providers

import asyncio
from typing import Annotated
from spritze import Container, Scope, provider, Depends, init, inject


class AsyncDatabaseConnection:
    def __init__(self, url: str) -> None:
        self.url: str = url
    
    async def query(self, sql: str) -> str:
        await asyncio.sleep(0.1)  # Simulate async operation
        return f"Async executed: {sql} on {self.url}"


class AsyncUserService:
    def __init__(self, db: AsyncDatabaseConnection) -> None:
        self.db: AsyncDatabaseConnection = db
    
    async def get_user(self, user_id: int) -> str:
        return await self.db.query(f"SELECT * FROM users WHERE id = {user_id}")


class AsyncContainer(Container):
    @provider(scope=Scope.APP)
    def db_url(self) -> str:
        return "postgresql://localhost/db"
    
    @provider(scope=Scope.REQUEST)
    async def database(self, url: str) -> AsyncDatabaseConnection:
        return AsyncDatabaseConnection(url)
    
    @provider(scope=Scope.REQUEST)
    async def user_service(self, db: AsyncDatabaseConnection) -> AsyncUserService:
        return AsyncUserService(db)


container = AsyncContainer()
init(container)


@inject
async def async_handler(
    user_id: int,
    service: Annotated[AsyncUserService, Depends()]
) -> str:
    return await service.get_user(user_id)


async def main() -> None:
    result = await async_handler(123)
    print(result)

if __name__ == "__main__":
    asyncio.run(main())

Context Managers

from typing import Annotated, Generator
from spritze import Container, Scope, provider, Depends, init, inject


class DatabaseConnection:
    def __init__(self, url: str) -> None:
        self.url: str = url
        print(f"Connected to {url}")
    
    def close(self) -> None:
        print(f"Disconnected from {self.url}")


class AppContainer(Container):
    @provider(scope=Scope.REQUEST)
    def database(self) -> Generator[DatabaseConnection, None, None]:
        db = DatabaseConnection("postgresql://localhost/db")
        try:
            yield db
        finally:
            db.close()


container = AppContainer()
init(container)


@inject
def handler(db: Annotated[DatabaseConnection, Depends()]) -> str:
    return f"Using database: {db.url}"


# Database will be automatically closed after the request
result = handler()

Advanced Features

Multiple Containers

from spritze import Container, init, inject, Scope, provider

class CoreContainer(Container):
    @provider(scope=Scope.APP)
    def config(self) -> str:
        return "core_config"

class FeatureContainer(Container):
    @provider(scope=Scope.REQUEST)
    def feature(self, config: str) -> str:
        return f"feature_with_{config}"

# Initialize multiple containers
init((CoreContainer(), FeatureContainer()))

@inject
def handler(feature: Annotated[str, Depends()]) -> str:
    return feature

Context Values

from spritze import Container, context, ContextField, Scope, provider

class RequestContext:
    def __init__(self, user_id: int) -> None:
        self.user_id: int = user_id

class AppContainer(Container):
    request: ContextField[RequestContext] = context.get(RequestContext)
    
    @provider(scope=Scope.REQUEST)
    def user_service(self, request: RequestContext) -> str:
        return f"User {request.user_id} service"

container = AppContainer()
container.context.update(RequestContext=RequestContext(user_id=123))

Examples

See examples/ for complete integrations:

  • Flask: examples/flask_example.py - Web application with request scoping
  • Litestar: examples/litestar_example.py - Modern async web framework
  • Django: examples/django_example.py - Traditional web framework
  • Multi-Container: examples/multi_container_example.py - Complex dependency graphs

Architecture

Spritze follows Clean Architecture and Domain-Driven Design principles:

  • Domain Layer: Core entities (Provider, Transient) and value objects (Scope, ProviderType)
  • Application Layer: Decorators and global container management
  • Infrastructure Layer: Context management and exception handling
  • Repository Layer: Container implementation and dependency resolution

License

Apache 2.0 — see LICENSE

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

spritze-0.3.6.tar.gz (26.1 kB view details)

Uploaded Source

Built Distribution

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

spritze-0.3.6-py3-none-any.whl (23.9 kB view details)

Uploaded Python 3

File details

Details for the file spritze-0.3.6.tar.gz.

File metadata

  • Download URL: spritze-0.3.6.tar.gz
  • Upload date:
  • Size: 26.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for spritze-0.3.6.tar.gz
Algorithm Hash digest
SHA256 d3a05e5d4b30dd611edc247ef7dd9fbdfd37dfc71d37472c9a6c218c3ea8dfb7
MD5 f96bf72bfe59173428f00d124bc00e4f
BLAKE2b-256 2537414427f5b19b796ec3b3662a0a363a69e2809077ea7db5cba95f2741e9ea

See more details on using hashes here.

Provenance

The following attestation bundles were made for spritze-0.3.6.tar.gz:

Publisher: release.yml on aSel1x/spritze

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

File details

Details for the file spritze-0.3.6-py3-none-any.whl.

File metadata

  • Download URL: spritze-0.3.6-py3-none-any.whl
  • Upload date:
  • Size: 23.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for spritze-0.3.6-py3-none-any.whl
Algorithm Hash digest
SHA256 f100fabfc8f3b29f187ab78b8a1172c96754fd1f551ad6d9015d34490213c199
MD5 21816d6ffd4d695fd887928d393d37ca
BLAKE2b-256 e34af7861b521e297dfcffcc70152412760b1a7f5c32c6fece1c2be883b63f17

See more details on using hashes here.

Provenance

The following attestation bundles were made for spritze-0.3.6-py3-none-any.whl:

Publisher: release.yml on aSel1x/spritze

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