CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-dishka

Cute DI framework with scopes and agreeable API for Python dependency injection

Pending
Overview
Eval results
Files

provider-system.mddocs/

Provider System

Modular provider classes and dependency registration functions for creating reusable dependency factories. Providers organize related dependencies and can be combined to build complex dependency graphs.

Capabilities

Provider Class

Main provider class for organizing and registering dependency factories with flexible configuration options.

class Provider(BaseProvider):
    def __init__(
        self, 
        scope: BaseScope | None = None, 
        component: Component | None = None
    ):
        """
        Create a new provider.
        
        Parameters:
        - scope: Default scope for dependencies registered in this provider
        - component: Default component for dependencies in this provider
        """
    
    def provide(
        self,
        source: Any,
        *,
        scope: BaseScope | None = None,
        provides: Any | None = None,
        cache: bool = True,
        recursive: bool = False,
        override: bool = False
    ) -> None:
        """
        Register a dependency factory.
        
        Parameters:
        - source: Class or factory function to provide the dependency
        - scope: Lifecycle scope (overrides provider default)
        - provides: Type to provide (inferred from source if not specified)
        - cache: Whether to cache instances within scope
        - recursive: Whether to recursively provide parent types
        - override: Whether to override existing registrations
        """
    
    def provide_all(
        self,
        *provides: Any,
        scope: BaseScope | None = None,
        cache: bool = True,
        recursive: bool = False,
        override: bool = False
    ) -> Callable[[Any], Any]:
        """
        Register multiple types from a single source.
        
        Parameters:
        - provides: Types to provide from the decorated source
        - scope: Lifecycle scope (overrides provider default)
        - cache: Whether to cache instances within scope
        - recursive: Whether to recursively provide parent types
        - override: Whether to override existing registrations
        
        Returns:
        Decorator function
        """
    
    def alias(
        self,
        source: Any,
        *,
        provides: Any | None = None,
        cache: bool = True,
        component: Component | None = None,
        override: bool = False
    ) -> None:
        """
        Create an alias for an existing dependency.
        
        Parameters:
        - source: Type of the existing dependency to alias
        - provides: Type to provide as alias (inferred if not specified)
        - cache: Whether to cache the alias
        - component: Component to look for source in
        - override: Whether to override existing registrations
        """
    
    def decorate(
        self,
        source: Any,
        *,
        provides: Any | None = None
    ) -> None:
        """
        Register a decorator for dependency modification.
        
        Parameters:
        - source: Decorator function that modifies the dependency
        - provides: Type to decorate (inferred if not specified)
        """
    
    def from_context(
        self,
        provides: Any,
        *,
        scope: BaseScope | None = None,
        override: bool = False
    ) -> None:
        """
        Register a context variable as a dependency.
        
        Parameters:
        - provides: Type to provide from context
        - scope: Lifecycle scope (overrides provider default)
        - override: Whether to override existing registrations
        """
    
    def to_component(self, component: Component) -> ProviderWrapper:
        """
        Create a wrapper that registers all dependencies in a specific component.
        
        Parameters:
        - component: Component identifier
        
        Returns:
        ProviderWrapper for the specified component
        """

Usage Example:

from dishka import Provider, Scope
from typing import Protocol

class Database(Protocol):
    def query(self, sql: str) -> list: ...

class PostgreSQLDB:
    def __init__(self, connection_string: str):
        self.connection_string = connection_string
    
    def query(self, sql: str) -> list:
        return []

class UserService:
    def __init__(self, db: Database):
        self.db = db

# Create provider
provider = Provider(scope=Scope.REQUEST)

# Register dependencies
provider.provide(PostgreSQLDB, provides=Database, scope=Scope.APP)
provider.provide(UserService)

# Register with alias
provider.alias(Database, provides=Protocol)

Provide Decorator

Function and decorator for registering dependency factories with flexible configuration.

def provide(
    source: Any = None,
    *,
    scope: BaseScope | None = None,
    provides: Any | None = None,
    cache: bool = True,
    recursive: bool = False,
    override: bool = False
) -> Any:
    """
    Register a dependency factory (can be used as decorator or direct call).
    
    Parameters:
    - source: Class or factory function (when used directly)
    - scope: Lifecycle scope for the dependency
    - provides: Type to provide (inferred from source if not specified)
    - cache: Whether to cache instances within scope
    - recursive: Whether to recursively provide parent types
    - override: Whether to override existing registrations
    
    Returns:
    When used as decorator: decorator function
    When used directly: CompositeDependencySource
    """

Usage Examples:

# As a decorator
@provide(scope=Scope.APP, provides=Database)
def create_database() -> PostgreSQLDB:
    return PostgreSQLDB("postgresql://localhost/db")

# On a class
@provide(scope=Scope.REQUEST)
class UserService:
    def __init__(self, db: Database):
        self.db = db

# Direct usage
provide(PostgreSQLDB, provides=Database, scope=Scope.APP)

# In a provider class
class DatabaseProvider(Provider):
    @provide(scope=Scope.APP)
    def connection_string(self) -> str:
        return "postgresql://localhost/db"
    
    @provide(scope=Scope.APP, provides=Database)
    def database(self, conn_str: str) -> PostgreSQLDB:
        return PostgreSQLDB(conn_str)

Provide All Decorator

Decorator for registering multiple types from a single source.

def provide_all(
    *provides: Any,
    scope: BaseScope | None = None,
    cache: bool = True,
    recursive: bool = False,
    override: bool = False
) -> Callable[[Any], CompositeDependencySource]:
    """
    Register multiple types from a single source.
    
    Parameters:
    - provides: Types to provide from the decorated source
    - scope: Lifecycle scope for all provided types
    - cache: Whether to cache instances within scope
    - recursive: Whether to recursively provide parent types
    - override: Whether to override existing registrations
    
    Returns:
    Decorator function
    """

Usage Example:

from typing import Protocol

class Readable(Protocol):
    def read(self) -> str: ...

class Writable(Protocol):
    def write(self, data: str) -> None: ...

@provide_all(Readable, Writable, scope=Scope.REQUEST)
class FileHandler:
    def __init__(self, filename: str):
        self.filename = filename
    
    def read(self) -> str:
        return "file contents"
    
    def write(self, data: str) -> None:
        pass

Alias Function

Function for creating dependency aliases to map interfaces to implementations.

def alias(
    source: Any,
    *,
    provides: Any | None = None,
    cache: bool = True,
    component: Component | None = None,
    override: bool = False
) -> CompositeDependencySource:
    """
    Create an alias for an existing dependency.
    
    Parameters:
    - source: Type of the existing dependency to alias
    - provides: Type to provide as alias (inferred if not specified)
    - cache: Whether to cache the alias
    - component: Component to look for source in
    - override: Whether to override existing registrations
    
    Returns:
    CompositeDependencySource for the alias
    """

Usage Example:

# Direct usage
alias(PostgreSQLDB, provides=Database)

# In a provider
provider = Provider()
provider.provide(PostgreSQLDB, scope=Scope.APP)
provider.alias(PostgreSQLDB, provides=Database)

# In a provider class
class DatabaseProvider(Provider):
    postgres_db = provide(PostgreSQLDB, scope=Scope.APP)
    database = alias(PostgreSQLDB, provides=Database)

Decorate Function

Function and decorator for registering dependency decorators that modify existing dependencies.

def decorate(
    source: Any = None,
    *,
    provides: Any | None = None
) -> Any:
    """
    Register a decorator for dependency modification.
    
    Parameters:
    - source: Decorator function (when used directly)
    - provides: Type to decorate (inferred if not specified)
    
    Returns:
    When used as decorator: decorator function  
    When used directly: CompositeDependencySource
    """

Usage Example:

# As a decorator
@decorate(provides=Database)
def add_logging(db: Database) -> Database:
    class LoggingDatabase:
        def __init__(self, wrapped_db: Database):
            self.db = wrapped_db
            
        def query(self, sql: str) -> list:
            print(f"Executing: {sql}")
            return self.db.query(sql)
    
    return LoggingDatabase(db)

# In a provider class
class DatabaseProvider(Provider):
    @decorate(provides=Database)
    def add_metrics(self, db: Database) -> Database:
        # Add metrics collection
        return MetricsDatabase(db)

Context Variable Registration

Function for registering context variables as injectable dependencies.

def from_context(
    provides: Any,
    *,
    scope: BaseScope | None = None,
    override: bool = False
) -> CompositeDependencySource:
    """
    Register a context variable as a dependency.
    
    Parameters:
    - provides: Type to provide from context
    - scope: Lifecycle scope for the context variable
    - override: Whether to override existing registrations
    
    Returns:
    CompositeDependencySource for the context variable
    """

Usage Example:

# Register context variables
from_context(str, scope=Scope.REQUEST)  # Get string from context
from_context(UserID, scope=Scope.REQUEST)  # Get UserID from context

# In a provider
provider = Provider()
provider.from_context(str, scope=Scope.REQUEST)

# In a provider class  
class ContextProvider(Provider):
    user_id = from_context(UserID, scope=Scope.REQUEST)
    request_id = from_context(RequestID, scope=Scope.REQUEST)

Base Provider Interface

Base interface that all providers implement.

class BaseProvider:
    """Base interface for dependency providers"""
    
    def get_dependencies(self) -> dict[DependencyKey, DependencySource]:
        """Get all dependencies registered in this provider"""

class ProviderWrapper(BaseProvider):
    """Wrapper that applies component to all dependencies"""
    
    def __init__(self, provider: BaseProvider, component: Component): ...

Factory Functions

Helper functions for creating root context providers.

def make_root_context_provider(
    context: dict[DependencyKey, Any]
) -> BaseProvider:
    """
    Create a provider that supplies values from a context dictionary.
    
    Parameters:
    - context: Dictionary mapping dependency keys to values
    
    Returns:
    BaseProvider that provides the context values
    """

Install with Tessl CLI

npx tessl i tessl/pypi-dishka

docs

component-system.md

container-management.md

framework-integrations.md

index.md

provider-system.md

scope-lifecycle.md

type-markers.md

validation-configuration.md

tile.json