Component Processor Metaprogramming
Overview
Traditional component processing systems hardcode assumptions about component names and processing patterns. The DynamicInterfaceMeta system eliminates these assumptions by generating abstract method interfaces dynamically from component enums.
# Generate processing interface for any component enum
ProcessorInterface = DynamicInterfaceMeta(
"CustomProcessor",
(ComponentProcessorInterface,),
{},
component_enum=MyComponents,
method_patterns=['process', 'validate', 'summarize']
)
# Interface automatically gets abstract methods:
# - process_well(), process_site(), process_channel()
# - validate_well(), validate_site(), validate_channel()
# - summarize_well(), summarize_site(), summarize_channel()
This enables the same processing framework to work with any component structure without manual interface definitions.
Dynamic Interface Generation
The metaclass generates abstract methods for each component × pattern combination.
class DynamicInterfaceMeta(type):
"""Metaclass that generates component processing interfaces."""
def __new__(mcs, name, bases, namespace, component_enum=None, method_patterns=None, **kwargs):
"""Create interface class with generated abstract methods."""
if component_enum and method_patterns:
# Generate abstract methods for each component × pattern combination
for component in component_enum:
for pattern in method_patterns:
method_name = f"{pattern}_{component.value}"
# Create abstract method dynamically
def create_abstract_method(method_name=method_name):
@abstractmethod
def abstract_method(self, context, **kwargs):
raise NotImplementedError(f"Method {method_name} must be implemented")
abstract_method.__name__ = method_name
return abstract_method
namespace[method_name] = create_abstract_method()
return super().__new__(mcs, name, bases, namespace)
This creates abstract methods that must be implemented by concrete processor classes.
Concrete Implementation
Concrete processors inherit from the generated interface and implement the abstract methods.
# Generate interface for specific component enum
MyProcessorInterface = DynamicInterfaceMeta(
"MyProcessorInterface",
(ComponentProcessorInterface,),
{},
component_enum=MyComponents,
method_patterns=['process', 'validate']
)
# Implement concrete processor
class MyProcessor(MyProcessorInterface):
def process_well(self, context, **kwargs):
# Process well data
pass
def process_site(self, context, **kwargs):
# Process site data
pass
def validate_well(self, context, **kwargs):
# Validate well configuration
pass
The metaclass ensures all required methods are implemented at class creation time.
Factory Pattern
The InterfaceGenerator provides cached interface creation for performance.
class InterfaceGenerator:
def __init__(self):
self._interface_cache: Dict[str, Type] = {}
def create_interface(self, component_enum: Type[T],
method_patterns: Optional[list] = None) -> Type:
"""Create component-specific interface class."""
cache_key = f"{component_enum.__name__}_{id(component_enum)}"
if cache_key in self._interface_cache:
return self._interface_cache[cache_key]
interface_class = DynamicInterfaceMeta(
f"{component_enum.__name__}ProcessorInterface",
(ComponentProcessorInterface,),
{},
component_enum=component_enum,
method_patterns=method_patterns or ['process', 'validate']
)
self._interface_cache[cache_key] = interface_class
return interface_class
This enables efficient creation of component-specific processing interfaces.
Usage Example
# Create interface for custom components
class AnalysisComponents(Enum):
SAMPLE = "sample"
CONDITION = "condition"
REPLICATE = "replicate"
generator = InterfaceGenerator()
AnalysisInterface = generator.create_interface(
AnalysisComponents,
method_patterns=['analyze', 'validate', 'export']
)
# Implement concrete processor
class SampleAnalyzer(AnalysisInterface):
def analyze_sample(self, context, **kwargs):
# Process sample data
pass
def validate_condition(self, context, **kwargs):
# Validate condition setup
pass
# Must implement all generated abstract methods
Common Gotchas:
All generated abstract methods must be implemented - missing methods cause initialization errors
Method patterns are fixed at interface creation time - can’t be changed later
Interface classes are cached by enum object ID - enum changes require new interfaces