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

Uploaded Python 3

File details

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

File metadata

  • Download URL: spritze-0.3.4.tar.gz
  • Upload date:
  • Size: 24.4 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.4.tar.gz
Algorithm Hash digest
SHA256 1950ce4c8e46fe45d1869b4a8d4ef5309e3f17b4c786f82e87679d5a9ce528f9
MD5 58df0f048f4eb45cbb61a58c5d828edc
BLAKE2b-256 0eab186cd8be8bb1467a2af069877051d7ea44c28fd7e15b33d992cd5ea00c20

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: spritze-0.3.4-py3-none-any.whl
  • Upload date:
  • Size: 23.5 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.4-py3-none-any.whl
Algorithm Hash digest
SHA256 2e09d1abef9970dfed57573f9e7fdebb6ac50080ef16fa34893e344d5a801712
MD5 cd5569899d5133105626fefe95b1d2d1
BLAKE2b-256 5d36f21c5ff9746c008ea3de290fa0b0a4ed3cb90b8a11bd26d0c10a0581108a

See more details on using hashes here.

Provenance

The following attestation bundles were made for spritze-0.3.4-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