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.12+ 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.8.tar.gz (29.3 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.8-py3-none-any.whl (24.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: spritze-0.3.8.tar.gz
  • Upload date:
  • Size: 29.3 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.8.tar.gz
Algorithm Hash digest
SHA256 2f2d49dfed48a99759ebe9130faa8cbfd90f50bdae915fdc87fb1c299a6c0c0d
MD5 c702a57a10e5315fca2332a4a132157f
BLAKE2b-256 af73100f08a6f325eaf2c749c0ca7408d5ebf0a8e0d7088005da7dd2834eed2b

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: spritze-0.3.8-py3-none-any.whl
  • Upload date:
  • Size: 24.8 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.8-py3-none-any.whl
Algorithm Hash digest
SHA256 981a36daaf470a6ceac7d778e275ef0784093a4fc6b489aff0b5d93ac2457640
MD5 44b1d4b0ca4c6cd89f9261d6c4332861
BLAKE2b-256 fda7640730d087e183d8285436914c581eb4a552f5ce5e310b489e41fd126b4d

See more details on using hashes here.

Provenance

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