rewrite property engine

Property class is gone; logic is now done with Layers, Stack and Filter
This commit is contained in:
Jakob Ketterl
2020-03-23 23:56:05 +01:00
parent 7562dc8ecb
commit c83d8580ba
12 changed files with 298 additions and 176 deletions

View File

@ -1,13 +1,18 @@
from abc import ABC, abstractmethod
import logging
logger = logging.getLogger(__name__)
class Subscription(object):
def __init__(self, subscriptee, subscriber):
def __init__(self, subscriptee, name, subscriber):
self.subscriptee = subscriptee
self.name = name
self.subscriber = subscriber
def getName(self):
return self.name
def call(self, *args, **kwargs):
self.subscriber(*args, **kwargs)
@ -15,30 +20,39 @@ class Subscription(object):
self.subscriptee.unwire(self)
class Property(object):
def __init__(self, value=None):
self.value = value
class PropertyManager(ABC):
def __init__(self):
self.subscribers = []
def getValue(self):
return self.value
@abstractmethod
def __getitem__(self, item):
pass
def setValue(self, value):
if self.value == value:
return self
self.value = value
for c in self.subscribers:
try:
c.call(self.value)
except Exception as e:
logger.exception(e)
return self
@abstractmethod
def __setitem__(self, key, value):
pass
@abstractmethod
def __contains__(self, item):
pass
@abstractmethod
def __dict__(self):
pass
def collect(self, *props):
return PropertyFilter(self, *props)
def wire(self, callback):
sub = Subscription(self, callback)
sub = Subscription(self, None, callback)
self.subscribers.append(sub)
if self.value is not None:
sub.call(self.value)
return sub
def wireProperty(self, name, callback):
sub = Subscription(self, name, callback)
self.subscribers.append(sub)
if name in self:
sub.call(self[name])
return sub
def unwire(self, sub):
@ -49,80 +63,79 @@ class Property(object):
pass
return self
def _fireCallbacks(self, name, value):
for c in self.subscribers:
try:
if c.getName() is None:
c.call(name, value)
elif c.getName() == name:
c.call(value)
except Exception as e:
logger.exception(e)
class PropertyManager(object):
def collect(self, *props):
return PropertyManager(
{name: self.getProperty(name) if self.hasProperty(name) else Property() for name in props}
)
class PropertyLayer(PropertyManager):
def __init__(self, properties=None):
super().__init__()
self.properties = {}
self.subscribers = []
if properties is not None:
for (name, prop) in properties.items():
self.add(name, prop)
self._add(name, prop)
def add(self, name, prop):
def _add(self, name, prop):
self.properties[name] = prop
def fireCallbacks(value):
for c in self.subscribers:
try:
c.call(name, value)
except Exception as e:
logger.exception(e)
prop.wire(fireCallbacks)
self._fireCallbacks(name, prop.getValue())
return self
def __contains__(self, name):
return self.hasProperty(name)
def __getitem__(self, name):
return self.getPropertyValue(name)
def __setitem__(self, name, value):
if not self.hasProperty(name):
self.add(name, Property())
self.getProperty(name).setValue(value)
def __dict__(self):
return {k: v.getValue() for k, v in self.properties.items()}
def hasProperty(self, name):
return name in self.properties
def getProperty(self, name):
if not self.hasProperty(name):
self.add(name, Property())
def __getitem__(self, name):
return self.properties[name]
def getPropertyValue(self, name):
return self.getProperty(name).getValue()
def __setitem__(self, name, value):
logger.debug("property change: %s => %s", name, value)
self.properties[name] = value
self._fireCallbacks(name, value)
def wire(self, callback):
sub = Subscription(self, callback)
self.subscribers.append(sub)
return sub
def unwire(self, sub):
try:
self.subscribers.remove(sub)
except ValueError:
# happens when already removed before
pass
return self
def defaults(self, other_pm):
for (key, p) in self.properties.items():
if p.getValue() is None:
p.setValue(other_pm[key])
return self
def __dict__(self):
return {k: v for k, v in self.properties.items()}
class PropertyLayers(object):
class PropertyFilter(PropertyManager):
def __init__(self, pm: PropertyManager, *props: str):
super().__init__()
self.pm = pm
self.props = props
self.pm.wire(self.receiveEvent)
def receiveEvent(self, name, value):
if name not in self.props:
return
self._fireCallbacks(name, value)
def __getitem__(self, item):
if item not in self.props:
raise KeyError(item)
return self.pm.__getitem__(item)
def __setitem__(self, key, value):
if key not in self.props:
raise KeyError(key)
return self.pm.__setitem__(key, value)
def __contains__(self, item):
if item not in self.props:
return False
return self.pm.__contains__(item)
def __dict__(self):
return {k: v for k, v in self.pm.__dict__().items() if k in self.props}
class PropertyStack(PropertyManager):
def __init__(self):
super().__init__()
self.layers = []
def addLayer(self, priority: int, pm: PropertyManager):
@ -136,8 +149,26 @@ class PropertyLayers(object):
if layer["props"] == pm:
self.layers.remove(layer)
def __getitem__(self, item):
def _getLayer(self, item):
layers = [la["props"] for la in sorted(self.layers, key=lambda l: l["priority"])]
for m in layers:
if item in m:
return m[item]
return m
# return top layer by default
return layers[0]
def __getitem__(self, item):
layer = self._getLayer(item)
return layer.__getitem__(item)
def __setitem__(self, key, value):
layer = self._getLayer(key)
return layer.__setitem__(key, value)
def __contains__(self, item):
layer = self._getLayer(item)
return layer.__contains__(item)
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}