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

Uploaded Python 3

File details

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

File metadata

  • Download URL: spritze-0.3.5.tar.gz
  • Upload date:
  • Size: 24.5 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.5.tar.gz
Algorithm Hash digest
SHA256 ee08513bd1946d7bc4766f6f02c0827ca1db39cbea4c90d863f5e7df0988260f
MD5 1806f6a2b7e0b736abcd4983c67b8702
BLAKE2b-256 14115c2b58b81ba20c8f7e0f3f2d8c6d7e5c7c0d41a4e7c3a4d556f937c47b85

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: spritze-0.3.5-py3-none-any.whl
  • Upload date:
  • Size: 23.7 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.5-py3-none-any.whl
Algorithm Hash digest
SHA256 8a1c61156c53fe43432c5cb392378b68a8278f919d111a7d9d4419e1465d7b60
MD5 9512d57582e2d800d7fdeffc11de8e49
BLAKE2b-256 024ab5d333a5e3452543d87f99a1c310ba6ed9059f1087a015d2e365ffcc60e9

See more details on using hashes here.

Provenance

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