change the list notification interface

This commit is contained in:
Jakob Ketterl 2022-12-14 01:07:20 +01:00
parent e7e5af9a53
commit f73c62c5df
2 changed files with 60 additions and 30 deletions

View File

@ -4,17 +4,32 @@ import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class ActiveListChange(ABC):
pass
class ActiveListIndexUpdated(ActiveListChange):
def __init__(self, index: int, oldValue, newValue):
self.index = index
self.oldValue = oldValue
self.newValue = newValue
class ActiveListIndexAppended(ActiveListChange):
def __init__(self, index: int, newValue):
self.index = index
self.newValue = newValue
class ActiveListIndexDeleted(ActiveListChange):
def __init__(self, index: int, oldValue):
self.index = index
self.oldValue = oldValue
class ActiveListListener(ABC): class ActiveListListener(ABC):
@abstractmethod @abstractmethod
def onIndexChanged(self, index, newValue): def onListChange(self, changes: list[ActiveListChange]):
pass
@abstractmethod
def onAppend(self, newValue):
pass
@abstractmethod
def onDelete(self, index):
pass pass
@ -35,30 +50,29 @@ class ActiveList:
def append(self, value): def append(self, value):
self.delegate.append(value) self.delegate.append(value)
self.__fireChanges([ActiveListIndexAppended(len(self) - 1, value)])
def __fireChanges(self, changes: list[ActiveListChange]):
for listener in self.listeners: for listener in self.listeners:
try: try:
listener.onAppend(value) listener.onListChange(changes)
except Exception: except Exception:
logger.exception("Exception during onAppend notification") logger.exception("Exception during onListChange notification")
def remove(self, value): def remove(self, value):
self.__delitem__(self.delegate.index(value)) self.__delitem__(self.delegate.index(value))
def __setitem__(self, key, value): def __setitem__(self, key, value):
if self.delegate[key] == value:
return
oldValue = self.delegate[key]
self.delegate[key] = value self.delegate[key] = value
for listener in self.listeners: self.__fireChanges([ActiveListIndexUpdated(key, oldValue, value)])
try:
listener.onIndexChanged(key, value)
except Exception:
logger.exception("Exception during onKeyChanged notification")
def __delitem__(self, key): def __delitem__(self, key):
oldValue = self.delegate[key]
del self.delegate[key] del self.delegate[key]
for listener in self.listeners: self.__fireChanges([ActiveListIndexDeleted(key, oldValue)])
try:
listener.onDelete(key)
except Exception:
logger.exception("Exception during onDelete notification")
def __getitem__(self, key): def __getitem__(self, key):
return self.delegate[key] return self.delegate[key]

View File

@ -1,4 +1,4 @@
from owrx.active.list import ActiveList from owrx.active.list import ActiveList, ActiveListIndexUpdated, ActiveListIndexAppended, ActiveListIndexDeleted
from unittest import TestCase from unittest import TestCase
from unittest.mock import Mock from unittest.mock import Mock
@ -22,17 +22,23 @@ class ActiveListTest(TestCase):
listenerMock = Mock() listenerMock = Mock()
list.addListener(listenerMock) list.addListener(listenerMock)
list[0] = "testvalue" list[0] = "testvalue"
listenerMock.onIndexChanged.assert_called_once_with(0, "testvalue") listenerMock.onListChange.assert_called_once()
changes, = listenerMock.onListChange.call_args.args
self.assertEqual(len(changes), 1)
self.assertIsInstance(changes[0], ActiveListIndexUpdated)
self.assertEqual(changes[0].index, 0)
self.assertEqual(changes[0].oldValue, "initialvalue")
self.assertEqual(changes[0].newValue, "testvalue")
def testListIndexChangeNotficationNotDisturbedByException(self): def testListIndexChangeNotficationNotDisturbedByException(self):
list = ActiveList(["initialvalue"]) list = ActiveList(["initialvalue"])
throwingMock = Mock() throwingMock = Mock()
throwingMock.onIndexChanged.side_effect = RuntimeError("this is a drill") throwingMock.onListChange.side_effect = RuntimeError("this is a drill")
list.addListener(throwingMock) list.addListener(throwingMock)
listenerMock = Mock() listenerMock = Mock()
list.addListener(listenerMock) list.addListener(listenerMock)
list[0] = "testvalue" list[0] = "testvalue"
listenerMock.onIndexChanged.assert_called_once_with(0, "testvalue") listenerMock.onListChange.assert_called_once()
def testListAppend(self): def testListAppend(self):
list = ActiveList() list = ActiveList()
@ -45,7 +51,12 @@ class ActiveListTest(TestCase):
listenerMock = Mock() listenerMock = Mock()
list.addListener(listenerMock) list.addListener(listenerMock)
list.append("testvalue") list.append("testvalue")
listenerMock.onAppend.assert_called_once_with("testvalue") listenerMock.onListChange.assert_called_once()
changes, = listenerMock.onListChange.call_args.args
self.assertEqual(len(changes), 1)
self.assertIsInstance(changes[0], ActiveListIndexAppended)
self.assertEqual(changes[0].index, 0)
self.assertEqual(changes[0].newValue, "testvalue")
def testListDelete(self): def testListDelete(self):
list = ActiveList(["value1", "value2"]) list = ActiveList(["value1", "value2"])
@ -53,12 +64,17 @@ class ActiveListTest(TestCase):
self.assertEqual(len(list), 1) self.assertEqual(len(list), 1)
self.assertEqual(list[0], "value2") self.assertEqual(list[0], "value2")
def testListDelteNotification(self): def testListDeleteNotification(self):
list = ActiveList(["value1", "value2"]) list = ActiveList(["value1", "value2"])
listenerMock = Mock() listenerMock = Mock()
list.addListener(listenerMock) list.addListener(listenerMock)
del list[0] del list[0]
listenerMock.onDelete.assert_called_once_with(0) listenerMock.onListChange.assert_called_once()
changes, = listenerMock.onListChange.call_args.args
self.assertEqual(len(changes), 1)
self.assertIsInstance(changes[0], ActiveListIndexDeleted)
self.assertEqual(changes[0].index, 0)
self.assertEqual(changes[0].oldValue, 'value1')
def testListDeleteByValue(self): def testListDeleteByValue(self):
list = ActiveList(["value1", "value2"]) list = ActiveList(["value1", "value2"])
@ -77,8 +93,8 @@ class ActiveListTest(TestCase):
listenerMock = Mock() listenerMock = Mock()
list.addListener(listenerMock) list.addListener(listenerMock)
list[0] = "testvalue" list[0] = "testvalue"
listenerMock.onIndexChanged.assert_called_once_with(0, "testvalue") listenerMock.onListChange.assert_called_once()
listenerMock.reset_mock() listenerMock.reset_mock()
list.removeListener(listenerMock) list.removeListener(listenerMock)
list[0] = "someothervalue" list[0] = "someothervalue"
listenerMock.onIndexChanged.assert_not_called() listenerMock.onListChange.assert_not_called()