create filtering that prevents overwriting the device name

This commit is contained in:
Jakob Ketterl 2021-02-24 00:09:57 +01:00
parent 4199a583f8
commit f69d78926e
8 changed files with 99 additions and 25 deletions

View File

@ -1,18 +1,21 @@
from owrx.config import Config from owrx.config import Config
from owrx.locator import Locator from owrx.locator import Locator
from owrx.property import PropertyFilter from owrx.property import PropertyFilter
from owrx.property.filter import ByPropertyName
class ReceiverDetails(PropertyFilter): class ReceiverDetails(PropertyFilter):
def __init__(self): def __init__(self):
super().__init__( super().__init__(
Config.get(), Config.get(),
"receiver_name", ByPropertyName(
"receiver_location", "receiver_name",
"receiver_asl", "receiver_location",
"receiver_gps", "receiver_asl",
"photo_title", "receiver_gps",
"photo_desc", "photo_title",
"photo_desc",
)
) )
def __dict__(self): def __dict__(self):

View File

@ -1,5 +1,6 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from owrx.property.validators import Validator from owrx.property.validators import Validator
from owrx.property.filter import Filter, ByPropertyName
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -60,7 +61,7 @@ class PropertyManager(ABC):
return self.__dict__().__len__() return self.__dict__().__len__()
def filter(self, *props): def filter(self, *props):
return PropertyFilter(self, *props) return PropertyFilter(self, ByPropertyName(*props))
def readonly(self): def readonly(self):
return PropertyReadOnly(self) return PropertyReadOnly(self)
@ -132,41 +133,41 @@ class PropertyLayer(PropertyManager):
class PropertyFilter(PropertyManager): class PropertyFilter(PropertyManager):
def __init__(self, pm: PropertyManager, *props: str): def __init__(self, pm: PropertyManager, filter: Filter):
super().__init__() super().__init__()
self.pm = pm self.pm = pm
self.props = props self._filter = filter
self.pm.wire(self.receiveEvent) self.pm.wire(self.receiveEvent)
def receiveEvent(self, changes): def receiveEvent(self, changes):
changesToForward = {name: value for name, value in changes.items() if name in self.props} changesToForward = {name: value for name, value in changes.items() if self._filter.apply(name)}
self._fireCallbacks(changesToForward) self._fireCallbacks(changesToForward)
def __getitem__(self, item): def __getitem__(self, item):
if item not in self.props: if not self._filter.apply(item):
raise KeyError(item) raise KeyError(item)
return self.pm.__getitem__(item) return self.pm.__getitem__(item)
def __setitem__(self, key, value): def __setitem__(self, key, value):
if key not in self.props: if not self._filter.apply(key):
raise KeyError(key) raise KeyError(key)
return self.pm.__setitem__(key, value) return self.pm.__setitem__(key, value)
def __contains__(self, item): def __contains__(self, item):
if item not in self.props: if not self._filter.apply(item):
return False return False
return self.pm.__contains__(item) return self.pm.__contains__(item)
def __dict__(self): def __dict__(self):
return {k: v for k, v in self.pm.__dict__().items() if k in self.props} return {k: v for k, v in self.pm.__dict__().items() if self._filter.apply(k)}
def __delitem__(self, key): def __delitem__(self, key):
if key not in self.props: if not self._filter.apply(key):
raise KeyError(key) raise KeyError(key)
return self.pm.__delitem__(key) return self.pm.__delitem__(key)
def keys(self): def keys(self):
return [k for k in self.pm.keys() if k in self.props] return [k for k in self.pm.keys() if self._filter.apply(k)]
class PropertyDelegator(PropertyManager): class PropertyDelegator(PropertyManager):

23
owrx/property/filter.py Normal file
View File

@ -0,0 +1,23 @@
from abc import ABC, abstractmethod
class Filter(ABC):
@abstractmethod
def apply(self, prop) -> bool:
pass
class ByPropertyName(Filter):
def __init__(self, *props):
self.props = props
def apply(self, prop) -> bool:
return prop in self.props
class ByLambda(Filter):
def __init__(self, func):
self.func = func
def apply(self, prop) -> bool:
return self.func(prop)

View File

@ -9,7 +9,8 @@ import signal
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from owrx.command import CommandMapper from owrx.command import CommandMapper
from owrx.socket import getAvailablePort from owrx.socket import getAvailablePort
from owrx.property import PropertyStack, PropertyLayer from owrx.property import PropertyStack, PropertyLayer, PropertyFilter
from owrx.property.filter import ByLambda
from owrx.form import Input, TextInput, NumberInput, CheckboxInput, ModesInput from owrx.form import Input, TextInput, NumberInput, CheckboxInput, ModesInput
from owrx.form.converter import OptionalConverter from owrx.form.converter import OptionalConverter
from owrx.form.device import GainInput from owrx.form.device import GainInput
@ -96,6 +97,9 @@ class SdrSource(ABC):
if self.isAlwaysOn() and self.state is not SdrSourceState.DISABLED: if self.isAlwaysOn() and self.state is not SdrSourceState.DISABLED:
self.start() self.start()
def _loadProfile(self, profile):
self.props.replaceLayer(0, PropertyFilter(profile, ByLambda(lambda x: x != "name")))
def validateProfiles(self): def validateProfiles(self):
props = PropertyStack() props = PropertyStack()
props.addLayer(1, self.props) props.addLayer(1, self.props)
@ -155,7 +159,7 @@ class SdrSource(ABC):
profile = profiles[profile_id] profile = profiles[profile_id]
self.profile_id = profile_id self.profile_id = profile_id
self.props.replaceLayer(0, profile) self._loadProfile(profile)
def getId(self): def getId(self):
return self.id return self.id

View File

View File

@ -0,0 +1,17 @@
from owrx.property.filter import ByLambda
from unittest import TestCase
from unittest.mock import Mock
class TestByLambda(TestCase):
def testPositive(self):
mock = Mock(return_value=True)
filter = ByLambda(mock)
self.assertTrue(filter.apply("test_key"))
mock.assert_called_with("test_key")
def testNegateive(self):
mock = Mock(return_value=False)
filter = ByLambda(mock)
self.assertFalse(filter.apply("test_key"))
mock.assert_called_with("test_key")

View File

@ -0,0 +1,12 @@
from owrx.property.filter import ByPropertyName
from unittest import TestCase
class ByPropertyNameTest(TestCase):
def testNameIsInList(self):
filter = ByPropertyName("test_key")
self.assertTrue(filter.apply("test_key"))
def testNameNotInList(self):
filter = ByPropertyName("test_key")
self.assertFalse(filter.apply("other_key"))

View File

@ -7,20 +7,26 @@ class PropertyFilterTest(TestCase):
def testPassesProperty(self): def testPassesProperty(self):
pm = PropertyLayer() pm = PropertyLayer()
pm["testkey"] = "testvalue" pm["testkey"] = "testvalue"
pf = PropertyFilter(pm, "testkey") mock = Mock()
mock.apply.return_value = True
pf = PropertyFilter(pm, mock)
self.assertEqual(pf["testkey"], "testvalue") self.assertEqual(pf["testkey"], "testvalue")
def testMissesProperty(self): def testMissesProperty(self):
pm = PropertyLayer() pm = PropertyLayer()
pm["testkey"] = "testvalue" pm["testkey"] = "testvalue"
pf = PropertyFilter(pm, "other_key") mock = Mock()
mock.apply.return_value = False
pf = PropertyFilter(pm, mock)
self.assertFalse("testkey" in pf) self.assertFalse("testkey" in pf)
with self.assertRaises(KeyError): with self.assertRaises(KeyError):
x = pf["testkey"] x = pf["testkey"]
def testForwardsEvent(self): def testForwardsEvent(self):
pm = PropertyLayer() pm = PropertyLayer()
pf = PropertyFilter(pm, "testkey") mock = Mock()
mock.apply.return_value = True
pf = PropertyFilter(pm, mock)
mock = Mock() mock = Mock()
pf.wire(mock.method) pf.wire(mock.method)
pm["testkey"] = "testvalue" pm["testkey"] = "testvalue"
@ -28,7 +34,9 @@ class PropertyFilterTest(TestCase):
def testForwardsPropertyEvent(self): def testForwardsPropertyEvent(self):
pm = PropertyLayer() pm = PropertyLayer()
pf = PropertyFilter(pm, "testkey") mock = Mock()
mock.apply.return_value = True
pf = PropertyFilter(pm, mock)
mock = Mock() mock = Mock()
pf.wireProperty("testkey", mock.method) pf.wireProperty("testkey", mock.method)
pm["testkey"] = "testvalue" pm["testkey"] = "testvalue"
@ -36,7 +44,9 @@ class PropertyFilterTest(TestCase):
def testForwardsWrite(self): def testForwardsWrite(self):
pm = PropertyLayer() pm = PropertyLayer()
pf = PropertyFilter(pm, "testkey") mock = Mock()
mock.apply.return_value = True
pf = PropertyFilter(pm, mock)
pf["testkey"] = "testvalue" pf["testkey"] = "testvalue"
self.assertTrue("testkey" in pm) self.assertTrue("testkey" in pm)
self.assertEqual(pm["testkey"], "testvalue") self.assertEqual(pm["testkey"], "testvalue")
@ -44,7 +54,9 @@ class PropertyFilterTest(TestCase):
def testOverwrite(self): def testOverwrite(self):
pm = PropertyLayer() pm = PropertyLayer()
pm["testkey"] = "old value" pm["testkey"] = "old value"
pf = PropertyFilter(pm, "testkey") mock = Mock()
mock.apply.return_value = True
pf = PropertyFilter(pm, mock)
pf["testkey"] = "new value" pf["testkey"] = "new value"
self.assertEqual(pm["testkey"], "new value") self.assertEqual(pm["testkey"], "new value")
self.assertEqual(pf["testkey"], "new value") self.assertEqual(pf["testkey"], "new value")
@ -52,7 +64,9 @@ class PropertyFilterTest(TestCase):
def testRejectsWrite(self): def testRejectsWrite(self):
pm = PropertyLayer() pm = PropertyLayer()
pm["testkey"] = "old value" pm["testkey"] = "old value"
pf = PropertyFilter(pm, "otherkey") mock = Mock()
mock.apply.return_value = False
pf = PropertyFilter(pm, mock)
with self.assertRaises(KeyError): with self.assertRaises(KeyError):
pf["testkey"] = "new value" pf["testkey"] = "new value"
self.assertEqual(pm["testkey"], "old value") self.assertEqual(pm["testkey"], "old value")