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

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for spritze-0.2.1.tar.gz
Algorithm Hash digest
SHA256 3f8ad19571b5c83ed4c649668b05ea634bb9c4e6728a5d68928d757836f99127
MD5 1163d970b1124d937a4c5a5e9c81376f
BLAKE2b-256 f98d703245d4404b4c66f24dfeed5483b1c38c2592b797c92f63555f7c2364d3

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: spritze-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 23.6 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.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 2e8364562f473fb80b8d8127b57cd2a447f4e77740eec04deb0a87c728b88beb
MD5 5742c250babd5a2017317683618b6f15
BLAKE2b-256 7612f41b94390a52d3024c418b5ab62cecd39e1df4ef92b0b4ddf4b0d5e54f9a

See more details on using hashes here.

Provenance

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