add layer add / remove events + tests
This commit is contained in:
		@@ -40,6 +40,10 @@ class PropertyManager(ABC):
 | 
			
		||||
    def __dict__(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def keys(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def collect(self, *props):
 | 
			
		||||
        return PropertyFilter(self, *props)
 | 
			
		||||
 | 
			
		||||
@@ -95,6 +99,9 @@ class PropertyLayer(PropertyManager):
 | 
			
		||||
    def __dict__(self):
 | 
			
		||||
        return {k: v for k, v in self.properties.items()}
 | 
			
		||||
 | 
			
		||||
    def keys(self):
 | 
			
		||||
        return self.properties.keys()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PropertyFilter(PropertyManager):
 | 
			
		||||
    def __init__(self, pm: PropertyManager, *props: str):
 | 
			
		||||
@@ -126,6 +133,9 @@ class PropertyFilter(PropertyManager):
 | 
			
		||||
    def __dict__(self):
 | 
			
		||||
        return {k: v for k, v in self.pm.__dict__().items() if k in self.props}
 | 
			
		||||
 | 
			
		||||
    def keys(self):
 | 
			
		||||
        return [k for k in self.pm.keys() if k in self.props]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PropertyStack(PropertyManager):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
@@ -136,6 +146,10 @@ class PropertyStack(PropertyManager):
 | 
			
		||||
        """
 | 
			
		||||
        highest priority = 0
 | 
			
		||||
        """
 | 
			
		||||
        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})
 | 
			
		||||
 | 
			
		||||
        def eventClosure(name, value):
 | 
			
		||||
@@ -152,6 +166,12 @@ class PropertyStack(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"])]
 | 
			
		||||
@@ -159,7 +179,8 @@ class PropertyStack(PropertyManager):
 | 
			
		||||
            if item in m:
 | 
			
		||||
                return m
 | 
			
		||||
        # return top layer by default
 | 
			
		||||
        return layers[0]
 | 
			
		||||
        if layers:
 | 
			
		||||
            return layers[0]
 | 
			
		||||
 | 
			
		||||
    def __getitem__(self, item):
 | 
			
		||||
        layer = self._getTopLayer(item)
 | 
			
		||||
@@ -171,8 +192,12 @@ class PropertyStack(PropertyManager):
 | 
			
		||||
 | 
			
		||||
    def __contains__(self, item):
 | 
			
		||||
        layer = self._getTopLayer(item)
 | 
			
		||||
        return layer.__contains__(item)
 | 
			
		||||
        if layer:
 | 
			
		||||
            return layer.__contains__(item)
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def __dict__(self):
 | 
			
		||||
        keys = [key for l in self.layers for key in l["props"].__dict__().keys()]
 | 
			
		||||
        return {k: self.__getitem__(k) for k in keys}
 | 
			
		||||
        return {k: self.__getitem__(k) for k in self.keys()}
 | 
			
		||||
 | 
			
		||||
    def keys(self):
 | 
			
		||||
        return set([key for l in self.layers for key in l["props"].keys()])
 | 
			
		||||
 
 | 
			
		||||
@@ -65,3 +65,74 @@ class PropertyStackTest(TestCase):
 | 
			
		||||
        mock.method.assert_not_called()
 | 
			
		||||
        high_layer["testkey"] = "modified high value"
 | 
			
		||||
        mock.method.assert_called_once_with("testkey", "modified high value")
 | 
			
		||||
 | 
			
		||||
    def testPropertyEventOnLayerAdd(self):
 | 
			
		||||
        low_layer = PropertyLayer()
 | 
			
		||||
        low_layer["testkey"] = "low value"
 | 
			
		||||
        stack = PropertyStack()
 | 
			
		||||
        stack.addLayer(1, low_layer)
 | 
			
		||||
        mock = Mock()
 | 
			
		||||
        stack.wireProperty("testkey", mock.method)
 | 
			
		||||
        mock.reset_mock()
 | 
			
		||||
        high_layer = PropertyLayer()
 | 
			
		||||
        high_layer["testkey"] = "high value"
 | 
			
		||||
        stack.addLayer(0, high_layer)
 | 
			
		||||
        mock.method.assert_called_once_with("high value")
 | 
			
		||||
 | 
			
		||||
    def testNoEventOnExistingValue(self):
 | 
			
		||||
        low_layer = PropertyLayer()
 | 
			
		||||
        low_layer["testkey"] = "same value"
 | 
			
		||||
        stack = PropertyStack()
 | 
			
		||||
        stack.addLayer(1, low_layer)
 | 
			
		||||
        mock = Mock()
 | 
			
		||||
        stack.wireProperty("testkey", mock.method)
 | 
			
		||||
        mock.reset_mock()
 | 
			
		||||
        high_layer = PropertyLayer()
 | 
			
		||||
        high_layer["testkey"] = "same value"
 | 
			
		||||
        stack.addLayer(0, high_layer)
 | 
			
		||||
        mock.method.assert_not_called()
 | 
			
		||||
 | 
			
		||||
    def testEventOnLayerWithNewProperty(self):
 | 
			
		||||
        low_layer = PropertyLayer()
 | 
			
		||||
        low_layer["existingkey"] = "existing value"
 | 
			
		||||
        stack = PropertyStack()
 | 
			
		||||
        stack.addLayer(1, low_layer)
 | 
			
		||||
        mock = Mock()
 | 
			
		||||
        stack.wireProperty("newkey", mock.method)
 | 
			
		||||
        high_layer = PropertyLayer()
 | 
			
		||||
        high_layer["newkey"] = "new value"
 | 
			
		||||
        stack.addLayer(0, high_layer)
 | 
			
		||||
        mock.method.assert_called_once_with("new value")
 | 
			
		||||
 | 
			
		||||
    def testEventOnLayerRemoval(self):
 | 
			
		||||
        low_layer = PropertyLayer()
 | 
			
		||||
        high_layer = PropertyLayer()
 | 
			
		||||
        stack = PropertyStack()
 | 
			
		||||
        stack.addLayer(1, low_layer)
 | 
			
		||||
        stack.addLayer(0, high_layer)
 | 
			
		||||
        low_layer["testkey"] = "low value"
 | 
			
		||||
        high_layer["testkey"] = "high value"
 | 
			
		||||
 | 
			
		||||
        mock = Mock()
 | 
			
		||||
        stack.wireProperty("testkey", mock.method)
 | 
			
		||||
        mock.method.assert_called_once_with("high value")
 | 
			
		||||
        mock.reset_mock()
 | 
			
		||||
        stack.removeLayer(high_layer)
 | 
			
		||||
        mock.method.assert_called_once_with("low value")
 | 
			
		||||
 | 
			
		||||
    def testNoneOnKeyRemoval(self):
 | 
			
		||||
        low_layer = PropertyLayer()
 | 
			
		||||
        high_layer = PropertyLayer()
 | 
			
		||||
        stack = PropertyStack()
 | 
			
		||||
        stack.addLayer(1, low_layer)
 | 
			
		||||
        stack.addLayer(0, high_layer)
 | 
			
		||||
        low_layer["testkey"] = "low value"
 | 
			
		||||
        high_layer["testkey"] = "high value"
 | 
			
		||||
        high_layer["unique key"] = "unique value"
 | 
			
		||||
 | 
			
		||||
        mock = Mock()
 | 
			
		||||
        stack.wireProperty("unique key", mock.method)
 | 
			
		||||
        mock.method.assert_called_once_with("unique value")
 | 
			
		||||
        mock.reset_mock()
 | 
			
		||||
        stack.removeLayer(high_layer)
 | 
			
		||||
        mock.method.assert_called_once_with(None)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user