Spaces:
Running
Running
| """DTO Validation Utilities | |
| Provides validation functions and utilities for DTOs, | |
| including custom validation decorators and error handling. | |
| """ | |
| from typing import Any, Callable, TypeVar, Union | |
| from functools import wraps | |
| import logging | |
| logger = logging.getLogger(__name__) | |
| T = TypeVar('T') | |
| class ValidationError(Exception): | |
| """Custom exception for DTO validation errors""" | |
| def __init__(self, message: str, field: str = None, value: Any = None): | |
| self.message = message | |
| self.field = field | |
| self.value = value | |
| super().__init__(self.message) | |
| def __str__(self): | |
| if self.field: | |
| return f"Validation error for field '{self.field}': {self.message}" | |
| return f"Validation error: {self.message}" | |
| def validate_dto(dto_instance: Any) -> bool: | |
| """Validate a DTO instance | |
| Args: | |
| dto_instance: The DTO instance to validate | |
| Returns: | |
| bool: True if validation passes | |
| Raises: | |
| ValidationError: If validation fails | |
| """ | |
| try: | |
| # Call the DTO's validation method if it exists | |
| if hasattr(dto_instance, '_validate'): | |
| dto_instance._validate() | |
| # Additional validation can be added here | |
| logger.debug(f"Successfully validated {type(dto_instance).__name__}") | |
| return True | |
| except ValueError as e: | |
| logger.error(f"Validation failed for {type(dto_instance).__name__}: {e}") | |
| raise ValidationError(str(e)) from e | |
| except Exception as e: | |
| logger.error(f"Unexpected error during validation of {type(dto_instance).__name__}: {e}") | |
| raise ValidationError(f"Validation failed: {e}") from e | |
| def validation_required(func: Callable[..., T]) -> Callable[..., T]: | |
| """Decorator to ensure DTO validation before method execution | |
| Args: | |
| func: The method to decorate | |
| Returns: | |
| Decorated function that validates 'self' before execution | |
| """ | |
| def wrapper(self, *args, **kwargs): | |
| try: | |
| validate_dto(self) | |
| return func(self, *args, **kwargs) | |
| except ValidationError: | |
| raise | |
| except Exception as e: | |
| raise ValidationError(f"Error in {func.__name__}: {e}") from e | |
| return wrapper | |
| def validate_field(value: Any, field_name: str, validator: Callable[[Any], bool], | |
| error_message: str = None) -> Any: | |
| """Validate a single field value | |
| Args: | |
| value: The value to validate | |
| field_name: Name of the field being validated | |
| validator: Function that returns True if value is valid | |
| error_message: Custom error message | |
| Returns: | |
| The validated value | |
| Raises: | |
| ValidationError: If validation fails | |
| """ | |
| try: | |
| if not validator(value): | |
| message = error_message or f"Invalid value for field '{field_name}'" | |
| raise ValidationError(message, field_name, value) | |
| return value | |
| except ValidationError: | |
| raise | |
| except Exception as e: | |
| raise ValidationError(f"Validation error for field '{field_name}': {e}", field_name, value) from e | |
| def validate_required(value: Any, field_name: str) -> Any: | |
| """Validate that a field is not None or empty | |
| Args: | |
| value: The value to validate | |
| field_name: Name of the field being validated | |
| Returns: | |
| The validated value | |
| Raises: | |
| ValidationError: If field is None or empty | |
| """ | |
| if value is None: | |
| raise ValidationError(f"Field '{field_name}' is required", field_name, value) | |
| if isinstance(value, (str, list, dict)) and len(value) == 0: | |
| raise ValidationError(f"Field '{field_name}' cannot be empty", field_name, value) | |
| return value | |
| def validate_type(value: Any, field_name: str, expected_type: Union[type, tuple]) -> Any: | |
| """Validate that a field is of the expected type | |
| Args: | |
| value: The value to validate | |
| field_name: Name of the field being validated | |
| expected_type: Expected type or tuple of types | |
| Returns: | |
| The validated value | |
| Raises: | |
| ValidationError: If type doesn't match | |
| """ | |
| if not isinstance(value, expected_type): | |
| if isinstance(expected_type, tuple): | |
| type_names = [t.__name__ for t in expected_type] | |
| expected_str = " or ".join(type_names) | |
| else: | |
| expected_str = expected_type.__name__ | |
| actual_type = type(value).__name__ | |
| raise ValidationError( | |
| f"Field '{field_name}' must be of type {expected_str}, got {actual_type}", | |
| field_name, value | |
| ) | |
| return value | |
| def validate_range(value: Union[int, float], field_name: str, | |
| min_value: Union[int, float] = None, | |
| max_value: Union[int, float] = None) -> Union[int, float]: | |
| """Validate that a numeric value is within a specified range | |
| Args: | |
| value: The numeric value to validate | |
| field_name: Name of the field being validated | |
| min_value: Minimum allowed value (inclusive) | |
| max_value: Maximum allowed value (inclusive) | |
| Returns: | |
| The validated value | |
| Raises: | |
| ValidationError: If value is outside the range | |
| """ | |
| if min_value is not None and value < min_value: | |
| raise ValidationError( | |
| f"Field '{field_name}' must be >= {min_value}, got {value}", | |
| field_name, value | |
| ) | |
| if max_value is not None and value > max_value: | |
| raise ValidationError( | |
| f"Field '{field_name}' must be <= {max_value}, got {value}", | |
| field_name, value | |
| ) | |
| return value | |
| def validate_choices(value: Any, field_name: str, choices: list) -> Any: | |
| """Validate that a value is one of the allowed choices | |
| Args: | |
| value: The value to validate | |
| field_name: Name of the field being validated | |
| choices: List of allowed values | |
| Returns: | |
| The validated value | |
| Raises: | |
| ValidationError: If value is not in choices | |
| """ | |
| if value not in choices: | |
| raise ValidationError( | |
| f"Field '{field_name}' must be one of {choices}, got '{value}'", | |
| field_name, value | |
| ) | |
| return value |