implement more list transformation events

This commit is contained in:
Jakob Ketterl 2023-05-09 17:42:03 +02:00
parent 24c4741215
commit 8a588270f6
3 changed files with 121 additions and 16 deletions

View file

@ -1,4 +1,6 @@
from abc import ABC, abstractmethod
from typing import Union
from functools import partial
import logging
logger = logging.getLogger(__name__)
@ -43,10 +45,29 @@ class ActiveListListener(ABC):
pass
class ActiveListTransformation(ABC):
@abstractmethod
def transform(self, value):
pass
def __call__(self, *args, **kwargs):
return self.transform(*args, **kwargs)
def monitor(self, member, callback: callable):
pass
def unmonitor(self, member):
pass
class ActiveListTransformationListener(ActiveListListener):
def __init__(self, transformation: callable, target: "ActiveList"):
def __init__(self, transformation: callable, source: "ActiveList", target: "ActiveList"):
self.transformation = transformation
self.source = source
self.target = target
if isinstance(transformation, ActiveListTransformation):
for idx, v in enumerate(self.source):
transformation.monitor(v, partial(self._onMonitor, idx))
def onListChange(self, source: "ActiveList", changes: list[ActiveListChange]):
for change in changes:
@ -57,6 +78,9 @@ class ActiveListTransformationListener(ActiveListListener):
elif isinstance(change, ActiveListIndexDeleted):
del self.target[change.index]
def _onMonitor(self, idx):
self.target[idx] = self.transformation(self.source[idx])
class ActiveListFilterListener(ActiveListListener):
def __init__(self, filter: callable, keyMap: list, target: "ActiveList"):
@ -91,21 +115,39 @@ class ActiveListFlattenListener(ActiveListListener):
def __init__(self, source: "ActiveList", target: "ActiveList"):
self.source = source
self.target = target
self.source.addListener(self)
for member in self.source:
member.addListener(self)
def getOffsetFor(self, source: "ActiveList"):
idx = self.source.index(source)
return self.getOffsetForIndex(idx)
def getOffsetForIndex(self, idx: int):
return sum(len(s) for s in self.source[0:idx])
def onListChange(self, source: "ActiveList", changes: list[ActiveListChange]):
for change in changes:
if isinstance(change, ActiveListIndexAdded):
self.target.insert(self.getOffsetFor(source) + change.index, change.newValue)
elif isinstance(change, ActiveListIndexUpdated):
self.target[self.getOffsetFor(source) + change.index] = change.newValue
elif isinstance(change, ActiveListIndexDeleted):
del self.target[self.getOffsetFor(source) + change.index]
if source is self.source:
if isinstance(change, ActiveListIndexAdded):
idx = self.getOffsetForIndex(change.index)
for n, v in enumerate(change.newValue):
self.target.insert(idx + n, v)
elif isinstance(change, ActiveListIndexUpdated):
idx = self.getOffsetForIndex(change.index)
del self.target[idx, idx + len(change.oldValue)]
for n, v in enumerate(change.newValue):
self.target.insert(idx + n, v)
elif isinstance(change, ActiveListIndexDeleted):
idx = self.getOffsetForIndex(change.index)
del self.target[idx, idx + len(change.oldValue)]
else:
if isinstance(change, ActiveListIndexAdded):
self.target.insert(self.getOffsetFor(source) + change.index, change.newValue)
elif isinstance(change, ActiveListIndexUpdated):
self.target[self.getOffsetFor(source) + change.index] = change.newValue
elif isinstance(change, ActiveListIndexDeleted):
del self.target[self.getOffsetFor(source) + change.index]
class ActiveList:
@ -144,9 +186,9 @@ class ActiveList:
def index(self, value):
return self.delegate.index(value)
def map(self, transform: callable):
def map(self, transform: Union[callable, ActiveListTransformation]):
res = ActiveList([transform(v) for v in self])
self.addListener(ActiveListTransformationListener(transform, res))
self.addListener(ActiveListTransformationListener(transform, self, res))
return res
def filter(self, filter: callable):
@ -172,9 +214,15 @@ class ActiveList:
self.__fireChanges([ActiveListIndexUpdated(key, oldValue, value)])
def __delitem__(self, key):
oldValue = self.delegate[key]
del self.delegate[key]
self.__fireChanges([ActiveListIndexDeleted(key, oldValue)])
if isinstance(key, tuple):
start, stop = key
changes = [ActiveListIndexDeleted(start + idx, v) for idx, v in enumerate(self.delegate[start:stop])]
del self.delegate[start:stop]
self.__fireChanges(changes)
else:
oldValue = self.delegate[key]
del self.delegate[key]
self.__fireChanges([ActiveListIndexDeleted(key, oldValue)])
def __getitem__(self, key):
return self.delegate[key]

View file

@ -191,7 +191,7 @@ class ActiveListTest(TestCase):
self.assertEqual(flattenedList[2], 3)
self.assertEqual(flattenedList[3], 4)
def testActiveFlattenAdd(self):
def testActiveFlattenMemberAppend(self):
sublist = ActiveList([3, 4])
list = ActiveList([ActiveList([1, 2]), sublist, ActiveList([6, 7])])
flattenedList = list.flatten()
@ -199,7 +199,7 @@ class ActiveListTest(TestCase):
self.assertEqual(len(flattenedList), 7)
self.assertEqual(flattenedList[4], 5)
def testActiveFlattenInsert(self):
def testActiveFlattenMemberInsert(self):
sublist = ActiveList([3, 5])
list = ActiveList([ActiveList([1, 2]), sublist, ActiveList([6, 7])])
flattenedList = list.flatten()
@ -207,7 +207,23 @@ class ActiveListTest(TestCase):
self.assertEqual(len(flattenedList), 7)
self.assertEqual(flattenedList[3], 4)
def testActiveFlattenUpdate(self):
def testActiveFlattenListInsert(self):
list = ActiveList([ActiveList([1, 2]), ActiveList([6, 7])])
flattenedList = list.flatten()
sublist = ActiveList([3, 4])
list.insert(1, sublist)
self.assertEqual(len(flattenedList), 6)
self.assertEqual(flattenedList[2], 3)
def testActiveFlattenListUpdate(self):
sublist = ActiveList([3, 9, 5])
list = ActiveList([ActiveList([1, 2]), sublist, ActiveList([6, 7])])
flattenedList = list.flatten()
sublist = ActiveList([3, 4, 5])
list[1] = sublist
self.assertEqual(flattenedList[3], 4)
def testActiveFlattenMemberUpdate(self):
sublist = ActiveList([3, 9, 5])
list = ActiveList([ActiveList([1, 2]), sublist, ActiveList([6, 7])])
flattenedList = list.flatten()
@ -215,10 +231,17 @@ class ActiveListTest(TestCase):
self.assertEqual(len(flattenedList), 7)
self.assertEqual(flattenedList[3], 4)
def testActiveFlattenDelete(self):
def testActiveFlattenMemberDelete(self):
sublist = ActiveList([3, 4, 9, 5])
list = ActiveList([ActiveList([1, 2]), sublist, ActiveList([6, 7])])
flattenedList = list.flatten()
del sublist[2]
self.assertEqual(len(flattenedList), 7)
self.assertEqual(flattenedList[4], 5)
def testActiveFlattenListDelete(self):
list = ActiveList([ActiveList([1, 2]), ActiveList([9]), ActiveList([3, 4]), ActiveList([5, 6])])
flattenedList = list.flatten()
del list[1]
self.assertEqual(len(flattenedList), 6)
self.assertEqual(flattenedList[2], 3)

View file

@ -0,0 +1,34 @@
from owrx.active.list import ActiveList, ActiveListTransformation
from unittest import TestCase
class TestTranformation(ActiveListTransformation):
def __init__(self):
self.callback = None
self.prefix = "value"
def transform(self, value):
return "{}{}".format(self.prefix, value)
def monitor(self, member, callback: callable):
self.callback = callback
def trigger(self, newPrefix: str):
self.prefix = newPrefix
self.callback()
class AdvancedTransformationTest(TestCase):
def testListAdvancedTransformation(self):
list = ActiveList([1, 2])
transformedList = list.map(TestTranformation())
self.assertEqual(len(transformedList), 2)
self.assertEqual(transformedList[0], "value1")
self.assertEqual(transformedList[1], "value2")
def testListMonitor(self):
list = ActiveList([1, 2])
transformation = TestTranformation()
transformedList = list.map(transformation)
transformation.trigger("foobar")
self.assertEqual(transformedList[1], "foobar2")