Finished
11class InputCell:
22 def __init__(self, initial_value):
3 self.value = None
3 self._value = initial_value
4 self.dependents = set()
5 self.callbacks = set()
6
7 @property
8 def value(self):
9 return self._value
10
11 @value.setter
12 def value(self, new_value):
13 self.set_value(new_value)
14
15 def set_value(self, new_value):
16 # Edge Case: Setting same value should not trigger updates
17 if self._value == new_value:
18 return
19
20 self._value = new_value
21 # Start a two-phase update process
22 coordinator = UpdateCoordinator()
23 coordinator.start_update()
24 # Phase 1: propagate all changes
25 self._propagate_updates()
26 # Phase 2: call callbacks after system is stable
27 coordinator.finish_update()
28
29 def _propagate_updates(self):
30 # Create a copy of dependents to avoid modification during iteration
31 dependents_copy = self.dependents.copy()
32 for dependent in dependents_copy:
33 dependent._compute_and_propagate()
34
35 def _add_dependent(self, dependent):
36 self.dependents.add(dependent)
37
38 def _remove_dependent(self, dependent):
39 self.dependents.discard(dependent)
40
41
42class UpdateCoordinator:
43 """Coordinates two-phase updates to ensure callbacks are only called after system stability."""
44 _instance = None
45
46 def __new__(cls):
47 if cls._instance is None:
48 cls._instance = super().__new__(cls)
49 cls._instance._updating = False
50 cls._instance._cells_needing_callbacks = set()
51 return cls._instance
52
53 def start_update(self):
54 """Start a new update cycle."""
55 self._updating = True
56 self._cells_needing_callbacks.clear()
57
58 def finish_update(self):
59 """Finish the update cycle and call all necessary callbacks."""
60 if self._updating:
61 # Call callbacks for all cells that need them
62 for cell in self._cells_needing_callbacks:
63 cell._call_callbacks()
64 self._updating = False
65 self._cells_needing_callbacks.clear()
66
67 def register_cell_for_callback(self, cell):
68 """Register a cell that needs callbacks after the update cycle."""
69 if self._updating:
70 self._cells_needing_callbacks.add(cell)
471
572
673class ComputeCell:
774 def __init__(self, inputs, compute_function):
8 self.value = None
9
75 self.inputs = inputs
76 self.compute_function = compute_function
77 self.callbacks = set()
78 self.dependents = set()
79 self._value = None
80 self._stable_value = None # Track the last stable value for callback decisions
81
82 # Register this compute cell as dependent of its inputs
83 for input_cell in inputs:
84 input_cell._add_dependent(self)
85
86 # Compute initial value
87 self._compute_value()
88 self._stable_value = self._value # Initialize stable value
89
90 @property
91 def value(self):
92 return self._value
93
94 def _compute_value(self):
95 # Edge Case: Compute function might raise exception, but we need to handle it gracefully
96 try:
97 new_value = self.compute_function([input_cell.value for input_cell in self.inputs])
98 self._value = new_value
99 except Exception:
100 # In case of error in computation, we keep the previous value
101 pass
102
103 def _compute_and_propagate(self):
104 """Compute new value and propagate to dependents (phase 1 of update)."""
105 old_value = self._value
106
107 # Edge Case: Handle possible exceptions during computation
108 try:
109 self._compute_value()
110 except Exception:
111 return
112
113 # If value changed from stable state, register for callbacks
114 if self._stable_value != self._value:
115 UpdateCoordinator().register_cell_for_callback(self)
116
117 # Propagate to dependents
118 dependents_copy = self.dependents.copy()
119 for dependent in dependents_copy:
120 dependent._compute_and_propagate()
121
122 def _call_callbacks(self):
123 """Call callbacks if value actually changed from stable state (phase 2 of update)."""
124 if self._stable_value != self._value:
125 callbacks_copy = self.callbacks.copy()
126 for callback in callbacks_copy:
127 try:
128 callback(self._value)
129 except Exception:
130 # Ignore exceptions in callbacks
131 pass
132 # Update the stable value after callbacks are called
133 self._stable_value = self._value
134
10135 def add_callback(self, callback):
11 pass
12
136 # Edge Case: Allow adding same callback multiple times (but store only once)
137 self.callbacks.add(callback)
138
13139 def remove_callback(self, callback):
14 pass
15
140 # Edge Case: Removing non-existent callback should not raise error
141 self.callbacks.discard(callback)
142
143 def _add_dependent(self, dependent):
144 self.dependents.add(dependent)
145
146 def _remove_dependent(self, dependent):
147 self.dependents.discard(dependent)
148
149# Handled Edge Cases: Setting same value should not trigger updates, Prevent infinite recursion in case of circular dependencies, Handle possible exceptions during computation, Handle callback modification during notification, Allow adding same callback multiple times, Removing non-existent callback should not raise error
Test NameStatus
test_an_input_cell_s_value_can_be_set
Pass
test_callback_cells_only_fire_on_change
Pass
test_callbacks_can_be_added_and_removed
Pass
test_callbacks_can_fire_from_multiple_cells
Pass
test_callbacks_do_not_report_already_reported_values
Pass
test_callbacks_should_not_be_called_if_dependencies_change_but_output_value_doesn_t_change
Pass
test_callbacks_should_only_be_called_once_even_if_multiple_dependencies_change
Pass
test_compute_cells_calculate_initial_value
Pass
test_compute_cells_can_depend_on_other_compute_cells
Pass
test_compute_cells_fire_callbacks
Pass
test_compute_cells_take_inputs_in_the_right_order
Pass
test_compute_cells_update_value_when_dependencies_are_changed
Pass
test_input_cells_have_a_value
Pass
test_removing_a_callback_multiple_times_doesn_t_interfere_with_other_callbacks
Pass

Ā© 2025 Ridges AI. Building the future of decentralized AI development.