|
| 1 | +from dataclasses import dataclass, field |
| 2 | +from typing import Dict, Any, Optional |
| 3 | + |
| 4 | + |
| 5 | +@dataclass |
| 6 | +class Field: |
| 7 | + """Represents a field in a dependency node with its type and readiness state.""" |
| 8 | + name: str |
| 9 | + type: type |
| 10 | + value: Any = None |
| 11 | + ready: bool = False |
| 12 | + |
| 13 | + def set_ready(self, is_ready: bool = True) -> None: |
| 14 | + """Set the readiness state of this field.""" |
| 15 | + self.ready = is_ready |
| 16 | + |
| 17 | + def set_value(self, value: Any, mark_ready: bool = True) -> None: |
| 18 | + """Set the value of this field and optionally mark it as ready.""" |
| 19 | + self.value = value |
| 20 | + if mark_ready: |
| 21 | + self.ready = True |
| 22 | + |
| 23 | + |
| 24 | +@dataclass |
| 25 | +class DependencyNode: |
| 26 | + """ |
| 27 | + A node with typed fields and readiness tracking. |
| 28 | +
|
| 29 | + Example usage: |
| 30 | + # Create a dependency node for a Person |
| 31 | + somestruct = DependencyNode(name="struct_1") |
| 32 | +
|
| 33 | + # Add fields with their types |
| 34 | + somestruct.add_field("field_1", str) |
| 35 | + somestruct.add_field("field_2", int) |
| 36 | + somestruct.add_field("field_3", str) |
| 37 | +
|
| 38 | + # Check if the node is ready (should be False initially) |
| 39 | + print(f"Is node ready? {somestruct.is_ready}") # False |
| 40 | +
|
| 41 | + # Set some field values |
| 42 | + somestruct.set_field_value("field_1", "someproperty") |
| 43 | + somestruct.set_field_value("field_2", 30) |
| 44 | +
|
| 45 | + # Check if the node is ready (still False because email is not ready) |
| 46 | + print(f"Is node ready? {somestruct.is_ready}") # False |
| 47 | +
|
| 48 | + # Set the last field and make the node ready |
| 49 | + somestruct.set_field_value("field_3", "anotherproperty") |
| 50 | +
|
| 51 | + # Now the node should be ready |
| 52 | + print(f"Is node ready? {somestruct.is_ready}") # True |
| 53 | +
|
| 54 | + # You can also mark a field as not ready |
| 55 | + somestruct.set_field_ready("field_3", False) |
| 56 | +
|
| 57 | + # Now the node is not ready again |
| 58 | + print(f"Is node ready? {somestruct.is_ready}") # False |
| 59 | +
|
| 60 | + # Get all field values |
| 61 | + print(somestruct.get_field_values()) # {'field_1': 'someproperty', 'field_2': 30, 'field_3': 'anotherproperty'} |
| 62 | +
|
| 63 | + # Get only ready fields |
| 64 | + ready_fields = somestruct.get_ready_fields() |
| 65 | + print(f"Ready fields: {[field.name for field in ready_fields.values()]}") # ['field_1', 'field_2'] |
| 66 | + """ |
| 67 | + name: str |
| 68 | + fields: Dict[str, Field] = field(default_factory=dict) |
| 69 | + _ready_cache: Optional[bool] = field(default=None, repr=False) |
| 70 | + |
| 71 | + def add_field(self, name: str, field_type: type, initial_value: Any = None, ready: bool = False) -> None: |
| 72 | + """Add a field to the node with an optional initial value and readiness state.""" |
| 73 | + self.fields[name] = Field(name=name, type=field_type, value=initial_value, ready=ready) |
| 74 | + # Invalidate readiness cache |
| 75 | + self._ready_cache = None |
| 76 | + |
| 77 | + def get_field(self, name: str) -> Field: |
| 78 | + """Get a field by name.""" |
| 79 | + return self.fields[name] |
| 80 | + |
| 81 | + def set_field_value(self, name: str, value: Any, mark_ready: bool = True) -> None: |
| 82 | + """Set a field's value and optionally mark it as ready.""" |
| 83 | + if name not in self.fields: |
| 84 | + raise KeyError(f"Field '{name}' does not exist in node '{self.name}'") |
| 85 | + |
| 86 | + self.fields[name].set_value(value, mark_ready) |
| 87 | + # Invalidate readiness cache |
| 88 | + self._ready_cache = None |
| 89 | + |
| 90 | + def set_field_ready(self, name: str, is_ready: bool = True) -> None: |
| 91 | + """Mark a field as ready or not ready.""" |
| 92 | + if name not in self.fields: |
| 93 | + raise KeyError(f"Field '{name}' does not exist in node '{self.name}'") |
| 94 | + |
| 95 | + self.fields[name].set_ready(is_ready) |
| 96 | + # Invalidate readiness cache |
| 97 | + self._ready_cache = None |
| 98 | + |
| 99 | + @property |
| 100 | + def is_ready(self) -> bool: |
| 101 | + """Check if the node is ready (all fields are ready).""" |
| 102 | + # Use cached value if available |
| 103 | + if self._ready_cache is not None: |
| 104 | + return self._ready_cache |
| 105 | + |
| 106 | + # Calculate readiness only when needed |
| 107 | + if not self.fields: |
| 108 | + self._ready_cache = False |
| 109 | + return False |
| 110 | + |
| 111 | + self._ready_cache = all(elem.ready for elem in self.fields.values()) |
| 112 | + return self._ready_cache |
| 113 | + |
| 114 | + def get_field_values(self) -> Dict[str, Any]: |
| 115 | + """Get a dictionary of field names to their values.""" |
| 116 | + return {name: elem.value for name, elem in self.fields.items()} |
| 117 | + |
| 118 | + def get_ready_fields(self) -> Dict[str, Field]: |
| 119 | + """Get all fields that are marked as ready.""" |
| 120 | + return {name: elem for name, elem in self.fields.items() if elem.ready} |
0 commit comments