diff --git a/owrx/property/__init__.py b/owrx/property/__init__.py index 5db390a..4454bfd 100644 --- a/owrx/property/__init__.py +++ b/owrx/property/__init__.py @@ -92,7 +92,6 @@ class PropertyLayer(PropertyManager): def __setitem__(self, name, value): if name in self.properties and self.properties[name] == value: return - logger.debug("property change: %s => %s", name, value) self.properties[name] = value self._fireCallbacks(name, value) @@ -146,33 +145,61 @@ class PropertyStack(PropertyManager): """ highest priority = 0 """ + self._fireChanges(self._addLayer(priority, pm)) + + def _addLayer(self, priority: int, pm: PropertyManager): + changes = {} for key in pm.keys(): if key not in self or self[key] != pm[key]: - self._fireCallbacks(key, pm[key]) - - self.layers.append({"priority": priority, "props": pm}) + changes[key] = pm[key] def eventClosure(name, value): self.receiveEvent(pm, name, value) - pm.wire(eventClosure) + sub = pm.wire(eventClosure) + + self.layers.append({"priority": priority, "props": pm, "sub": sub}) + + return changes + + def removeLayer(self, pm: PropertyManager): + for layer in self.layers: + if layer["props"] == pm: + self._fireChanges(self._removeLayer(layer)) + + def _removeLayer(self, layer): + layer["sub"].cancel() + self.layers.remove(layer) + changes = {} + pm = layer["props"] + for key in pm.keys(): + if key in self: + if self[key] != pm[key]: + changes[key] = self[key] + else: + changes[key] = None + return changes + + def replaceLayer(self, priority: int, pm: PropertyManager): + layers = [x for x in self.layers if x["priority"] == priority] + changes = {} + if layers: + changes = self._removeLayer(layers[0]) + + for k, v in self._addLayer(priority, pm).items(): + changes[k] = v + + self._fireChanges(changes) + + def _fireChanges(self, changes): + for k, v in changes.items(): + self._fireCallbacks(k, v) def receiveEvent(self, layer, name, value): if layer != self._getTopLayer(name): return self._fireCallbacks(name, value) - def removeLayer(self, pm: PropertyManager): - for layer in self.layers: - if layer["props"] == pm: - self.layers.remove(layer) - for key in pm.keys(): - if key in self: - if self[key] != pm[key]: - self._fireCallbacks(key, self[key]) - else: - self._fireCallbacks(key, None) - def _getTopLayer(self, item): layers = [la["props"] for la in sorted(self.layers, key=lambda l: l["priority"])] for m in layers: diff --git a/test/property/test_property_stack.py b/test/property/test_property_stack.py index b2658ef..c86ae67 100644 --- a/test/property/test_property_stack.py +++ b/test/property/test_property_stack.py @@ -136,3 +136,34 @@ class PropertyStackTest(TestCase): mock.reset_mock() stack.removeLayer(high_layer) mock.method.assert_called_once_with(None) + + def testReplaceLayer(self): + first_layer = PropertyLayer() + first_layer["testkey"] = "old value" + second_layer = PropertyLayer() + second_layer["testkey"] = "new value" + + stack = PropertyStack() + stack.addLayer(0, first_layer) + + mock = Mock() + stack.wireProperty("testkey", mock.method) + mock.method.assert_called_once_with("old value") + mock.reset_mock() + + stack.replaceLayer(0, second_layer) + mock.method.assert_called_once_with("new value") + + def testUnwiresEventsOnRemoval(self): + layer = PropertyLayer() + layer["testkey"] = "before" + stack = PropertyStack() + stack.addLayer(0, layer) + mock = Mock() + stack.wire(mock.method) + stack.removeLayer(layer) + mock.method.assert_called_once_with("testkey", None) + mock.reset_mock() + + layer["testkey"] = "after" + mock.method.assert_not_called()