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

validation-configuration.mddocs/

Validation and Configuration

Comprehensive validation system for dependency graphs with configurable validation rules and error detection. The validation system ensures dependency graph integrity and helps catch configuration errors early.

Capabilities

Validation Settings

Configuration class for controlling dependency graph validation behavior with granular control over different types of checks.

class ValidationSettings:
    """Configuration for dependency graph validation"""
    
    nothing_overridden: bool = False
    """
    Check that override=True is used consistently.
    When True, validates that dependencies marked with override=True
    actually override existing registrations.
    """
    
    implicit_override: bool = False
    """
    Check for implicit overrides (same dependency registered multiple times).
    When True, validates that duplicate registrations are intentional
    by requiring explicit override=True.
    """
    
    nothing_decorated: bool = True
    """
    Check that decorator pattern is used correctly.
    When True, validates that decorators actually decorate existing dependencies.
    """
    
    def __init__(
        self,
        *,
        nothing_overridden: bool = False,
        implicit_override: bool = False, 
        nothing_decorated: bool = True
    ): ...

Usage Examples:

from dishka import ValidationSettings, make_container

# Default validation (lenient)
default_settings = ValidationSettings()
container = make_container(provider, validation_settings=default_settings)

# Custom validation settings
custom_settings = ValidationSettings(
    nothing_overridden=True,   # Require explicit overrides
    implicit_override=True,    # Catch duplicate registrations
    nothing_decorated=False    # Allow decorators without existing dependencies
)
container = make_container(provider, validation_settings=custom_settings)

# Individual setting adjustments
settings = ValidationSettings()
settings.nothing_overridden = True  # Enable override validation

Strict Validation

Predefined strict validation configuration with all checks enabled for development and testing environments.

STRICT_VALIDATION: ValidationSettings
"""
Strict validation settings with all checks enabled.
Equivalent to ValidationSettings(
    nothing_overridden=True,
    implicit_override=True, 
    nothing_decorated=True
)
"""

DEFAULT_VALIDATION: ValidationSettings
"""
Default validation settings used when no validation_settings specified.
Equivalent to ValidationSettings(
    nothing_overridden=False,
    implicit_override=False,
    nothing_decorated=True
)
"""

Usage Example:

from dishka import STRICT_VALIDATION, make_container

# Use strict validation for development
container = make_container(
    provider,
    validation_settings=STRICT_VALIDATION,
    skip_validation=False  # Ensure validation runs
)

# Strict validation will catch:
# - Overrides without override=True
# - Duplicate registrations without explicit override
# - Decorators without existing dependencies

Container Validation Options

Container creation functions support validation configuration and can skip validation entirely for performance.

def make_container(
    *providers: BaseProvider,
    skip_validation: bool = False,
    validation_settings: ValidationSettings = DEFAULT_VALIDATION,
    **kwargs
) -> Container:
    """
    Parameters:
    - skip_validation: Skip all dependency graph validation
    - validation_settings: Configuration for validation rules
    """

def make_async_container(
    *providers: BaseProvider,
    skip_validation: bool = False,
    validation_settings: ValidationSettings = DEFAULT_VALIDATION,
    **kwargs
) -> AsyncContainer:
    """
    Parameters:
    - skip_validation: Skip all dependency graph validation  
    - validation_settings: Configuration for validation rules
    """

Validation Examples:

# Skip validation for performance (production)
fast_container = make_container(
    provider,
    skip_validation=True  # No validation overhead
)

# Enable validation with custom settings (development)
dev_container = make_container(
    provider,
    skip_validation=False,
    validation_settings=STRICT_VALIDATION
)

# Default validation (moderate checking)
container = make_container(provider)  # Uses DEFAULT_VALIDATION

Override Validation

Validation for explicit override usage to prevent accidental duplicate registrations.

Override Rules:

  1. override=False (default): Registration fails if dependency already exists
  2. override=True: Registration replaces existing dependency
  3. nothing_overridden=True: Validates that override=True actually overrides something

Valid Override Usage:

from dishka import Provider

provider = Provider()

# Initial registration
provider.provide(PostgreSQLDatabase, provides=Database)

# Explicit override
provider.provide(
    MySQLDatabase, 
    provides=Database,
    override=True  # Required to replace existing registration
)

# This works with validation
container = make_container(
    provider,
    validation_settings=ValidationSettings(nothing_overridden=True)
)

Invalid Override Usage:

provider = Provider()

# Override without existing registration
provider.provide(
    Database,
    override=True  # Nothing to override!
)

# This fails with nothing_overridden=True
try:
    container = make_container(
        provider,
        validation_settings=ValidationSettings(nothing_overridden=True)
    )
except ValidationError as e:
    print(f"Override validation failed: {e}")

Implicit Override Detection

Detection of duplicate registrations that may be unintentional.

Implicit Override Rules:

When implicit_override=True:

  • Duplicate registrations without override=True raise validation errors
  • Helps catch accidental duplicate registrations
  • Enforces explicit intent for dependency replacement

Valid Explicit Overrides:

provider = Provider()

# Initial registration
provider.provide(InMemoryCache, provides=CacheService)

# Explicit override (valid)
provider.provide(
    RedisCache,
    provides=CacheService, 
    override=True  # Explicit intent to replace
)

# Validation passes
container = make_container(
    provider,
    validation_settings=ValidationSettings(implicit_override=True)
)

Invalid Implicit Overrides:

provider = Provider()

# Initial registration
provider.provide(InMemoryCache, provides=CacheService)

# Implicit override (invalid with implicit_override=True)
provider.provide(RedisCache, provides=CacheService)  # Missing override=True

# This fails validation
try:
    container = make_container(
        provider,
        validation_settings=ValidationSettings(implicit_override=True)
    )
except ValidationError as e:
    print(f"Implicit override detected: {e}")

Decorator Validation

Validation for decorator pattern usage to ensure decorators have dependencies to decorate.

Decorator Rules:

When nothing_decorated=True:

  • Decorators must decorate existing dependencies
  • Prevents decorators that have nothing to decorate
  • Ensures decorator pattern is used correctly

Valid Decorator Usage:

from dishka import Provider, provide, decorate

provider = Provider()

# Base dependency
@provider.provide
def database() -> Database:
    return PostgreSQLDatabase()

# Decorator for existing dependency (valid)
@provider.decorate(provides=Database)
def add_logging(db: Database) -> Database:
    return LoggingDatabase(db)

# Validation passes
container = make_container(
    provider,
    validation_settings=ValidationSettings(nothing_decorated=True)
)

Invalid Decorator Usage:

provider = Provider()

# Decorator without base dependency (invalid)
@provider.decorate(provides=Database)
def add_logging(db: Database) -> Database:
    return LoggingDatabase(db)  # Nothing to decorate!

# This fails validation
try:
    container = make_container(
        provider,
        validation_settings=ValidationSettings(nothing_decorated=True)
    )
except ValidationError as e:
    print(f"Decorator validation failed: {e}")

Validation Errors

Exception classes for different types of validation failures.

class ValidationError(DishkaError):
    """Base class for validation errors"""

class GraphMissingFactoryError(ValidationError):
    """Dependency required but no factory found"""

class DependencyCycleError(ValidationError):
    """Circular dependency detected in graph"""

class InvalidGraphError(ValidationError):
    """General dependency graph validation failure"""

class UnsupportedFactoryError(ValidationError):
    """Factory type not supported by container"""

Error Handling:

from dishka import ValidationError, make_container

try:
    container = make_container(
        provider,
        validation_settings=STRICT_VALIDATION
    )
except ValidationError as e:
    print(f"Validation failed: {e}")
    # Handle validation error - fix provider configuration
except DependencyCycleError as e:
    print(f"Circular dependency: {e}")
    # Handle circular dependency - restructure dependencies

Performance Considerations

Validation impact on container creation and runtime performance.

Validation Overhead:

import time
from dishka import make_container

# Measure validation overhead
start = time.time()
container_with_validation = make_container(
    provider,
    skip_validation=False,
    validation_settings=STRICT_VALIDATION
)
validation_time = time.time() - start

start = time.time()
container_without_validation = make_container(
    provider,
    skip_validation=True
)
no_validation_time = time.time() - start

print(f"With validation: {validation_time:.3f}s")
print(f"Without validation: {no_validation_time:.3f}s")

Production Recommendations:

# Development: Use strict validation
if DEBUG:
    container = make_container(
        provider,
        validation_settings=STRICT_VALIDATION
    )
else:
    # Production: Skip validation for performance
    container = make_container(
        provider,
        skip_validation=True
    )

Custom Validation Rules

While Dishka doesn't provide custom validation rule APIs, you can implement validation logic in providers.

Provider-Level Validation:

class ValidatingProvider(Provider):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._validate_configuration()
    
    def _validate_configuration(self):
        """Custom provider validation logic"""
        # Check provider-specific rules
        dependencies = self.get_dependencies()
        
        # Example: Ensure all database dependencies have proper scope
        for key, source in dependencies.items():
            if "Database" in str(key.type_hint):
                if source.scope != Scope.APP:
                    raise ValueError(f"Database {key} must use APP scope")
    
    @provide(scope=Scope.APP)
    def database(self) -> Database:
        return PostgreSQLDatabase()

Validation Best Practices

Recommended practices for using validation effectively.

1. Development vs Production:

# Use strict validation in development
DEV_SETTINGS = STRICT_VALIDATION

# Use minimal validation in production
PROD_SETTINGS = ValidationSettings(
    nothing_overridden=False,  # Allow flexibility
    implicit_override=False,   # Performance
    nothing_decorated=True     # Basic safety
)

settings = DEV_SETTINGS if DEBUG else PROD_SETTINGS
container = make_container(provider, validation_settings=settings)

2. Incremental Validation:

# Start with lenient settings and gradually increase strictness
settings = ValidationSettings(
    nothing_overridden=True,   # Start with override validation
    implicit_override=False,   # Add later when codebase is clean  
    nothing_decorated=True     # Basic decorator validation
)

3. CI/CD Integration:

# Always use strict validation in tests
def test_container_creation():
    """Ensure container can be created with strict validation"""
    container = make_container(
        test_provider,
        validation_settings=STRICT_VALIDATION
    )
    assert container is not None

4. Error Reporting:

def create_container_with_detailed_errors(provider):
    """Helper that provides detailed validation error information"""
    try:
        return make_container(
            provider,
            validation_settings=STRICT_VALIDATION
        )
    except ValidationError as e:
        print(f"Dependency validation failed:")
        print(f"Error: {e}")
        print(f"Provider: {provider}")
        print("Check your dependency registrations and fix the issues above.")
        raise

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