observer: Finish migrating Subject

This commit is contained in:
June Tate-Gans 2021-05-02 23:48:20 -05:00
parent 9a9f504995
commit 0a0ecfc9bc
2 changed files with 18 additions and 97 deletions

View File

@ -4,80 +4,7 @@ import unittest
from enum import Enum from enum import Enum
from g13gui.observer.subject import Subject
class ChangeType(Enum):
ADD = 0
REMOVE = 1
MODIFY = 2
class Subject(object):
"""Simple class to handle the subject-side of the Observer pattern."""
AllKeys = ''
def registerObserver(self, observer, subscribedKeys=AllKeys):
"""Registers an Observer class as an observer of this object"""
if subscribedKeys != Subject.AllKeys:
subscribedKeys = frozenset(subscribedKeys)
if '_observers' not in self.__dict__:
self._observers = {observer: subscribedKeys}
else:
self._observers[observer] = subscribedKeys
def removeObserver(self, observer):
"""Removes an observer from this object"""
if '_observers' in self.__dict__:
if observer in self._observers:
del self._observers[observer]
def addChange(self, type, key, data=None):
"""Schedules a change notification for transmitting later.
type[ChangeType]: the type of change that occurred.
key[string]: a required name for what field changed.
data[object]: an optional context-dependent object, dict, or
None, specifying what changed. In the case of an ADD or MODIFY,
whatChanged should be the new data. In the case of a DELETE, it should
be the old data (or None).
"""
if '_changes' not in self.__dict__:
self._changes = [(type, key, data)]
else:
self._changes.append((type, key, data))
def clearChanges(self):
"""Removes all scheduled changes from the change buffer."""
self._changes = []
def notifyChange(self):
raise NotImplementedError('Use Subject.notifyChanged instead')
def notifyChanged(self):
"""Notifies all observers of scheduled changes in the change buffer.
This method actually does the work of iterating through all observers
and all changes and delivering them to the Observer's onSubjectChanged
method.
It is safe to call this if there are no changes to send in the buffer,
or there are no observers to send changes to. Note that calling this
when no observers are registered will still flush the change buffer.
"""
if '_observers' in self.__dict__ and '_changes' in self.__dict__:
for observer, subscribedKeys in self._observers.items():
for type, key, data in self._changes:
if subscribedKeys == Subject.AllKeys or key in subscribedKeys:
observer.onSubjectChanged(self, type, key, data)
self._changes = []
def setProperty(self, propertyName, value, notify=True):
propertyName = '_' + propertyName
self.__dict__[propertyName] = value
self.addChange(ChangeType.MODIFY, propertyName, value)
if notify:
self.notifyChanged()
class Observer(object): class Observer(object):
@ -114,12 +41,8 @@ class Observer(object):
should be the old data (or None). should be the old data (or None).
""" """
if '_changeTriggers' not in self.__dict__: if '_changeTriggers' not in self.__dict__:
raise NotImplementedError( return
'onSubjectChanged(%s, %s, %s, %s) fired with no '
'listeners registered!' %
(subject, changeType, key, data))
found = False
triggers = ( triggers = (
self._changeTriggers.get((None, Subject.AllKeys)), self._changeTriggers.get((None, Subject.AllKeys)),
self._changeTriggers.get((changeType, Subject.AllKeys)), self._changeTriggers.get((changeType, Subject.AllKeys)),
@ -128,14 +51,8 @@ class Observer(object):
for trigger in triggers: for trigger in triggers:
if trigger: if trigger:
found = True
trigger(subject, changeType, key, data) trigger(subject, changeType, key, data)
if not found:
raise NotImplementedError(
'onSubjectChanged(%s, %s, %s, %s) fired without a listener!'
% (subject, changeType, key, data))
class ObserverTestCase(Observer, unittest.TestCase): class ObserverTestCase(Observer, unittest.TestCase):
def __init__(self, methodName): def __init__(self, methodName):

View File

@ -1,17 +1,21 @@
#!/usr/bin/python #!/usr/bin/python
import unittest import unittest
import observer
from builtins import property from builtins import property
from g13gui.observer.observer import Observer
from g13gui.observer.observer import ObserverTestCase
from g13gui.observer.subject import Subject
from g13gui.observer.subject import ChangeType
class TestIncorrectObserver(observer.Observer):
class TestIncorrectObserver(Observer):
def __hash__(self): def __hash__(self):
return hash('TestIncorrectObserver') return hash('TestIncorrectObserver')
class TestSubject(observer.Subject): class TestSubject(Subject):
def __init__(self): def __init__(self):
self._value = None self._value = None
@ -24,7 +28,7 @@ class TestSubject(observer.Subject):
self.setProperty('value', value) self.setProperty('value', value)
class ObserverTestCase(observer.ObserverTestCase): class ObserverTestCase(ObserverTestCase):
def setUp(self): def setUp(self):
self.subject = TestSubject() self.subject = TestSubject()
@ -42,7 +46,7 @@ class ObserverTestCase(observer.ObserverTestCase):
def testSubclassNotificationError(self): def testSubclassNotificationError(self):
testObserver = TestIncorrectObserver() testObserver = TestIncorrectObserver()
self.subject.addChange(observer.ChangeType.ADD, 'foo') self.subject.addChange(ChangeType.ADD, 'foo')
self.subject.registerObserver(testObserver) self.subject.registerObserver(testObserver)
try: try:
@ -55,25 +59,25 @@ class ObserverTestCase(observer.ObserverTestCase):
def testSubclassNotification(self): def testSubclassNotification(self):
self.subject.registerObserver(self) self.subject.registerObserver(self)
self.subject.addChange(observer.ChangeType.ADD, 'foo', 'bar') self.subject.addChange(ChangeType.ADD, 'foo', 'bar')
self.subject.notifyChanged() self.subject.notifyChanged()
self.assertChangeCount(1) self.assertChangeCount(1)
self.assertChangeNotified(self.subject, observer.ChangeType.ADD, 'foo') self.assertChangeNotified(self.subject, ChangeType.ADD, 'foo')
self.assertChangeDataEquals('bar') self.assertChangeDataEquals('bar')
def testSubscribedKeys(self): def testSubscribedKeys(self):
self.subject.registerObserver(self, {'a', 'b'}) self.subject.registerObserver(self, {'a', 'b'})
self.subject.addChange(observer.ChangeType.ADD, 'a') self.subject.addChange(ChangeType.ADD, 'a')
self.subject.addChange(observer.ChangeType.ADD, 'b') self.subject.addChange(ChangeType.ADD, 'b')
self.subject.addChange(observer.ChangeType.ADD, 'c') self.subject.addChange(ChangeType.ADD, 'c')
self.subject.notifyChanged() self.subject.notifyChanged()
self.assertChangeCount(2) self.assertChangeCount(2)
self.assertChangeNotified(self.subject, observer.ChangeType.ADD, 'a') self.assertChangeNotified(self.subject, ChangeType.ADD, 'a')
self.nextChange() self.nextChange()
self.assertChangeNotified(self.subject, observer.ChangeType.ADD, 'b') self.assertChangeNotified(self.subject, ChangeType.ADD, 'b')
self.nextChange() self.nextChange()