Component Validation System
Overview
Traditional validation systems hardcode assumptions about component names and valid combinations. The GenericValidator eliminates these assumptions by deriving validation rules from component configuration metadata.
class GenericValidator(Generic[T]):
def __init__(self, config: ComponentConfiguration[T]):
self.config = config
def validate_step(self, variable_components: List[T], group_by: Optional[T]) -> ValidationResult:
# Core constraint: group_by ∉ variable_components
self.config.validate_combination(variable_components, group_by)
This enables the same validation logic to work with any component configuration - wells, timepoints, batches - without code changes.
Core Constraint Validation
The system validates the fundamental processing constraint that prevents ambiguous behavior.
def validate_step(self, variable_components: List[T], group_by: Optional[T],
func_pattern: Any, step_name: str) -> ValidationResult:
"""Validate step configuration using generic rules."""
try:
# Core constraint: group_by ∉ variable_components
self.config.validate_combination(variable_components, group_by)
# Dict pattern requirements
if isinstance(func_pattern, dict) and not group_by:
return ValidationResult(
is_valid=False,
error_message=f"Dict pattern requires group_by in step '{step_name}'"
)
return ValidationResult(is_valid=True)
except ValueError as e:
return ValidationResult(is_valid=False, error_message=str(e))
The constraint ensures that the component used for function routing is not also used for data grouping.
Integration with Compilation
The validation system integrates with the OpenHCS compilation pipeline to provide early constraint checking.
# In FuncStepContractValidator.validate_funcstep()
config = get_openhcs_config()
validator = GenericValidator(config)
# Check for constraint violation: group_by ∈ variable_components
if step.group_by and step.group_by.value in [vc.value for vc in step.variable_components]:
# Auto-resolve constraint violation by nullifying group_by
logger.warning(f"Step '{step_name}': Auto-resolved group_by conflict")
step.group_by = None
This prevents invalid configurations from reaching the execution phase.
Extension Examples
Custom Validation Rules
class ExtendedValidator(GenericValidator):
"""Extended validator with custom constraints."""
def validate_step(self, variable_components, group_by, func_pattern, step_name):
"""Extended validation with custom constraints."""
# Run base validation
result = super().validate_step(variable_components, group_by, func_pattern, step_name)
if not result.is_valid:
return result
# Add custom validation logic
if self._has_custom_constraint_violation(variable_components, group_by):
return ValidationResult(
is_valid=False,
error_message=f"Custom constraint violation in step '{step_name}'"
)
return ValidationResult(is_valid=True)
Common Gotchas:
Don’t use
GroupBy.NONEwith dict patterns - validation will failComponent keys are cached on initialization - call
clear_component_cache()if input directory changesDict pattern keys must match actual component values, not enum names