diff --git a/owrx/property/__init__.py b/owrx/property/__init__.py index ee1d614..ace4d32 100644 --- a/owrx/property/__init__.py +++ b/owrx/property/__init__.py @@ -5,6 +5,10 @@ import logging logger = logging.getLogger(__name__) +class PropertyError(Exception): + pass + + class Subscription(object): def __init__(self, subscriptee, name, subscriber): self.subscriptee = subscriptee @@ -52,6 +56,9 @@ class PropertyManager(ABC): def filter(self, *props): return PropertyFilter(self, *props) + def readonly(self): + return PropertyReadOnly(self) + def wire(self, callback): sub = Subscription(self, None, callback) self.subscribers.append(sub) @@ -155,35 +162,16 @@ class PropertyFilter(PropertyManager): return [k for k in self.pm.keys() if k in self.props] -class PropertyValidationError(Exception): - def __init__(self, key, value): - super().__init__('Invalid value for property "{key}": "{value}"'.format(key=key, value=str(value))) - - -class PropertyValidator(PropertyManager): - def __init__(self, pm: PropertyManager, validators=None): +class PropertyDelegator(PropertyManager): + def __init__(self, pm: PropertyManager): self.pm = pm self.pm.wire(self._fireCallbacks) - if validators is None: - self.validators = {} - else: - self.validators = {k: Validator.of(v) for k, v in validators.items()} super().__init__() - def validate(self, key, value): - if key not in self.validators: - return - if not self.validators[key].isValid(value): - raise PropertyValidationError(key, value) - - def setValidator(self, key, validator): - self.validators[key] = Validator.of(validator) - def __getitem__(self, item): return self.pm.__getitem__(item) def __setitem__(self, key, value): - self.validate(key, value) return self.pm.__setitem__(key, value) def __contains__(self, item): @@ -199,6 +187,43 @@ class PropertyValidator(PropertyManager): return self.pm.keys() +class PropertyValidationError(PropertyError): + def __init__(self, key, value): + super().__init__('Invalid value for property "{key}": "{value}"'.format(key=key, value=str(value))) + + +class PropertyValidator(PropertyDelegator): + def __init__(self, pm: PropertyManager, validators=None): + super().__init__(pm) + if validators is None: + self.validators = {} + else: + self.validators = {k: Validator.of(v) for k, v in validators.items()} + + def validate(self, key, value): + if key not in self.validators: + return + if not self.validators[key].isValid(value): + raise PropertyValidationError(key, value) + + def setValidator(self, key, validator): + self.validators[key] = Validator.of(validator) + + def __setitem__(self, key, value): + self.validate(key, value) + return self.pm.__setitem__(key, value) + + +class PropertyWriteError(PropertyError): + def __init__(self, key): + super().__init__('Key "{key}" is not writeable'.format(key=key)) + + +class PropertyReadOnly(PropertyDelegator): + def __setitem__(self, key, value): + raise PropertyWriteError(key) + + class PropertyStack(PropertyManager): def __init__(self): super().__init__() diff --git a/test/property/test_property_readonly.py b/test/property/test_property_readonly.py new file mode 100644 index 0000000..a6e74f7 --- /dev/null +++ b/test/property/test_property_readonly.py @@ -0,0 +1,15 @@ +from unittest import TestCase +from owrx.property import PropertyLayer, PropertyReadOnly, PropertyWriteError + + +class PropertyReadOnlyTest(TestCase): + def testPreventsWrites(self): + layer = PropertyLayer() + layer["testkey"] = "initial value" + ro = PropertyReadOnly(layer) + with self.assertRaises(PropertyWriteError): + ro["testkey"] = "new value" + with self.assertRaises(PropertyWriteError): + ro["otherkey"] = "testvalue" + self.assertEqual(ro["testkey"], "initial value") + self.assertNotIn("otherkey", ro)