Skip to content

Commit

Permalink
Merge pull request #337 from robotools/dispatcher-enhancements
Browse files Browse the repository at this point in the history
Dispatcher enhancements
  • Loading branch information
typemytype committed Jan 26, 2021
2 parents cfd6a6b + 05b7889 commit 81006f0
Show file tree
Hide file tree
Showing 5 changed files with 288 additions and 24 deletions.
43 changes: 40 additions & 3 deletions Lib/defcon/objects/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def _get_dispatcher(self):

dispatcher = property(_get_dispatcher, doc="The :class:`defcon.tools.notifications.NotificationCenter` assigned to the parent of this object.")

def addObserver(self, observer, methodName, notification):
def addObserver(self, observer, methodName, notification, identifier=None):
"""
Add an observer to this object's notification dispatcher.
Expand All @@ -84,6 +84,10 @@ def addObserver(self, observer, methodName, notification):
when the notification is posted.
* **notification** The notification that the observer should
be notified of.
* **identifier** None or a string identifying the observation.
There is no requirement that the string be unique. A reverse
domain naming scheme is recommended, but there are no
requirements for the structure of the string.
The method that will be called as a result of the action
must accept a single *notification* argument. This will
Expand All @@ -93,12 +97,12 @@ def addObserver(self, observer, methodName, notification):
dispatcher = anObject.dispatcher
dispatcher.addObserver(observer=observer, methodName=methodName,
notification=notification, observable=anObject)
notification=notification, observable=anObject, identifier=identifier)
"""
dispatcher = self.dispatcher
if dispatcher is not None:
self.dispatcher.addObserver(observer=observer, methodName=methodName,
notification=notification, observable=self)
notification=notification, observable=self, identifier=identifier)

def removeObserver(self, observer, notification):
"""
Expand Down Expand Up @@ -218,6 +222,39 @@ def postNotification(self, notification, data=None):
if dispatcher is not None:
dispatcher.postNotification(notification=notification, observable=self, data=data)

def findObservations(self, observer=None, notification=None, observable=None, identifier=None):
"""
Find observations of this object matching the given
arguments based on the values that were passed during
addObserver. A value of None for any of these indicates
that all should be considered to match the value.
In the case of identifier, strings will be matched
using fnmatch.fnmatchcase. The returned value will be
a list of dictionaries with this format:
[
{
observer=<...>
observable=<...>
methodName="..."
notification="..."
identifier="..."
}
]
This is a convenience method that does the same thing as::
dispatcher = anObject.dispatcher
dispatcher.findObservations(
observer=observer, observable=anObject,
notification=notification, identifier=identifier
)
"""
dispatcher = self.dispatcher
if dispatcher is not None:
return self.dispatcher.findObservations(observer=observer, notification=notification,
observable=self, identifier=identifier)

# ------------------------
# Notification Observation
# ------------------------
Expand Down
8 changes: 7 additions & 1 deletion Lib/defcon/test/objects/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,18 @@ def test_addObserver(self):
obj._dispatcher = weakref.ref(notificationCenter)
obj.addObserver(observer=notificationObject,
methodName="notificationCallback",
notification="BaseObject.Changed")
notification="BaseObject.Changed",
identifier="test")
self.assertTrue(
obj.dispatcher.hasObserver(observer=notificationObject,
notification="BaseObject.Changed",
observable=obj)
)
expected = [
dict(observer=notificationObject, notification="BaseObject.Changed", observable=obj, identifier="test")
]
result = obj.findObservations()
self.assertEqual(expected, result)

def test_removeObserver(self):
obj = BaseObject()
Expand Down
6 changes: 5 additions & 1 deletion Lib/defcon/test/testTools.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,13 @@ def closeTestFontAsFileSystem(fileSystem, testFontPath=None):

class NotificationTestObserver(object):

def __init__(self):
def __init__(self, name=None):
self.name = name
self.stack = []

def __repr__(self):
return "<_TestObservable {name} {id}".format(name=self.name, id=id(self))

def notificationCallback(self, notification):
print(notification.name, notification.object.name)
self.stack.append((notification.name, notification.object.name))
Expand Down
140 changes: 135 additions & 5 deletions Lib/defcon/test/tools/test_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ def __init__(self, center, name):
self.center = center
self.name = name

def __repr__(self):
return "<_TestObservable {name} {id}".format(name=self.name, id=id(self))

def postNotification(self, name):
self.center.postNotification(name, self)

Expand Down Expand Up @@ -68,6 +71,30 @@ def test_removeObserver_notification_observable(self):
center.removeObserver(observer, "A", observable)
self.assertFalse(center.hasObserver(observer, "A", observable))

def test_removeObserver_noNotification(self):
center = NotificationCenter()
observable1 = _TestObservable(center, "Observable1")
observable2 = _TestObservable(center, "Observable2")
observer1 = NotificationTestObserver()
observer2 = NotificationTestObserver()
center.addObserver(observer1, "notificationCallback", "A", observable1)
center.addObserver(observer1, "notificationCallback", "B", observable1)
center.addObserver(observer2, "notificationCallback", "A", observable1)
center.addObserver(observer2, "notificationCallback", "B", observable1)
center.addObserver(observer1, "notificationCallback", "A", observable2)
center.addObserver(observer1, "notificationCallback", "B", observable2)
center.addObserver(observer2, "notificationCallback", "A", observable2)
center.addObserver(observer2, "notificationCallback", "B", observable2)
center.removeObserver(observer1, None, observable1)
self.assertFalse(center.hasObserver(observer1, "A", observable1))
self.assertFalse(center.hasObserver(observer1, "B", observable1))
self.assertTrue(center.hasObserver(observer1, "A", observable2))
self.assertTrue(center.hasObserver(observer1, "B", observable2))
self.assertTrue(center.hasObserver(observer2, "A", observable1))
self.assertTrue(center.hasObserver(observer2, "B", observable1))
self.assertTrue(center.hasObserver(observer2, "A", observable2))
self.assertTrue(center.hasObserver(observer2, "B", observable2))

def test_removeObserver_notification_no_observable(self):
center = NotificationCenter()
_TestObservable(center, "Observable")
Expand Down Expand Up @@ -124,7 +151,6 @@ def test_postNotification_notification_no_observable(self):
center.postNotification("B", observable2)
self.assertEqual(observer.stack[-1], ("A", "Observable2"))


def test_postNotification_no_notification_observable(self):
# no notification, observable
center = NotificationCenter()
Expand All @@ -139,7 +165,6 @@ def test_postNotification_no_notification_observable(self):
self.assertEqual(observer.stack[-1], ("B", "Observable1"))
center.postNotification("B", observable2)


def test_postNotification_no_notification_no_observable(self):
# no notification, no observable
center = NotificationCenter()
Expand All @@ -165,7 +190,6 @@ def test_hold_and_releaseHeldNotifications_all_notifications(self):
center.addObserver(observer, "notificationCallback", "A", observable1)
center.addObserver(observer, "notificationCallback", "B", observable1)
center.addObserver(observer, "notificationCallback", "C", observable2)

center.holdNotifications()
observable1.postNotification("A")
self.assertEqual(len(observer.stack), 0)
Expand All @@ -190,7 +214,6 @@ def test_hold_and_releaseHeldNotifications_notifications_of_observable(self):
center.addObserver(observer, "notificationCallback", "A", observable1)
center.addObserver(observer, "notificationCallback", "B", observable1)
center.addObserver(observer, "notificationCallback", "C", observable2)

center.holdNotifications(observable=observable1)
observable1.postNotification("A")
observable1.postNotification("A")
Expand All @@ -212,7 +235,6 @@ def test_hold_and_releaseHeldNotifications_notifications_of_observable(
center.addObserver(observer, "notificationCallback", "A", observable1)
center.addObserver(observer, "notificationCallback", "B", observable1)
center.addObserver(observer, "notificationCallback", "C", observable2)

center.holdNotifications(notification="A")
observable1.postNotification("A")
observable1.postNotification("A")
Expand Down Expand Up @@ -397,6 +419,114 @@ def test_areNotificationsDisabled_observer_off(self):
center.enableNotifications(observer=observer1)
self.assertFalse(center.areNotificationsDisabled(observer=observer1))

def test_addObserver_identifier(self):
center = NotificationCenter()
observable = _TestObservable(center, "Observable")
observer = NotificationTestObserver()
center.addObserver(observer, "notificationCallback", "A", observable, identifier="identifier1")
center.addObserver(observer, "notificationCallback", "B", observable, identifier="identifier2")
expected = [
dict(observer=observer, observable=observable, notification="A", identifier="identifier1"),
dict(observer=observer, observable=observable, notification="B", identifier="identifier2")
]
self.assertEqual(center.findObservations(), expected)

def test_addObserver_identifierDuplicate(self):
center = NotificationCenter()
observable = _TestObservable(center, "Observable")
observer = NotificationTestObserver()
center.addObserver(observer, "notificationCallback", "A", observable, identifier="identifier1")
center.addObserver(observer, "notificationCallback", "B", observable, identifier="identifier1")
expected = [
dict(observer=observer, observable=observable, notification="A", identifier="identifier1"),
dict(observer=observer, observable=observable, notification="B", identifier="identifier1")
]
self.assertEqual(center.findObservations(), expected)

def test_removeObserver_identifier(self):
center = NotificationCenter()
observable = _TestObservable(center, "Observable")
observer = NotificationTestObserver()
center.addObserver(observer, "notificationCallback", "A", observable, identifier="identifier1")
center.addObserver(observer, "notificationCallback", "B", observable, identifier="identifier2")
center.removeObserver(observer, "A", observable)
center.removeObserver(observer, "B", observable)
expected = []
self.assertEqual(center.findObservations(), expected)

def _buildFindObservationsObjects(self):
center = NotificationCenter()
observable1 = _TestObservable(center, "Observable1")
observable2 = _TestObservable(center, "Observable2")
observer1 = NotificationTestObserver(name="Observer1")
observer2 = NotificationTestObserver(name="Observer2")
center.addObserver(observer1, "notificationCallback", "A", observable1, identifier="identifier1-1-A")
center.addObserver(observer1, "notificationCallback", "B", observable1, identifier="identifier1-1-B")
center.addObserver(observer1, "notificationCallback", "A", observable2, identifier="identifier1-2-A")
center.addObserver(observer1, "notificationCallback", "B", observable2, identifier="identifier1-2-B")
center.addObserver(observer2, "notificationCallback", "A", observable1, identifier="identifier2-1-A")
center.addObserver(observer2, "notificationCallback", "B", observable1, identifier="identifier2-1-B")
center.addObserver(observer2, "notificationCallback", "A", observable2, identifier="identifier2-2-A")
center.addObserver(observer2, "notificationCallback", "B", observable2, identifier="identifier2-2-B")
return center, observer1, observer2, observable1, observable2

def test_findObservations_observer(self):
center, observer1, observer2, observable1, observable2 = self._buildFindObservationsObjects()
expected = [
dict(observer=observer1, notification="A", observable=observable1, identifier="identifier1-1-A"),
dict(observer=observer1, notification="B", observable=observable1, identifier="identifier1-1-B"),
dict(observer=observer1, notification="A", observable=observable2, identifier="identifier1-2-A"),
dict(observer=observer1, notification="B", observable=observable2, identifier="identifier1-2-B")
]
result = center.findObservations(observer=observer1)
self.assertEqual(result, expected)

def test_findObservations_observable(self):
center, observer1, observer2, observable1, observable2 = self._buildFindObservationsObjects()
expected = [
dict(observer=observer1, notification="A", observable=observable1, identifier="identifier1-1-A"),
dict(observer=observer2, notification="A", observable=observable1, identifier="identifier2-1-A"),
dict(observer=observer1, notification="B", observable=observable1, identifier="identifier1-1-B"),
dict(observer=observer2, notification="B", observable=observable1, identifier="identifier2-1-B")
]
result = center.findObservations(observable=observable1)
self.assertEqual(result, expected)

def test_findObservations_notification(self):
center, observer1, observer2, observable1, observable2 = self._buildFindObservationsObjects()
expected = [
dict(observer=observer1, notification="A", observable=observable1, identifier="identifier1-1-A"),
dict(observer=observer2, notification="A", observable=observable1, identifier="identifier2-1-A"),
dict(observer=observer1, notification="A", observable=observable2, identifier="identifier1-2-A"),
dict(observer=observer2, notification="A", observable=observable2, identifier="identifier2-2-A")
]
result = center.findObservations(notification="A")
self.assertEqual(result, expected)

def test_findObservations_identifier(self):
center, observer1, observer2, observable1, observable2 = self._buildFindObservationsObjects()
expected = [
dict(observer=observer1, notification="A", observable=observable1, identifier="identifier1-1-A")
]
result = center.findObservations(identifier="identifier1-1-A")
self.assertEqual(result, expected)

def test_findObservations_identifierPattern(self):
center, observer1, observer2, observable1, observable2 = self._buildFindObservationsObjects()
expected = [
dict(observer=observer1, notification="A", observable=observable1, identifier="identifier1-1-A"),
dict(observer=observer1, notification="A", observable=observable2, identifier="identifier1-2-A")
]
result = center.findObservations(identifier="identifier1-*-A")
self.assertEqual(result, expected)

def test_findObservations_all(self):
center, observer1, observer2, observable1, observable2 = self._buildFindObservationsObjects()
expected = [
dict(observer=observer1, notification="A", observable=observable1, identifier="identifier1-1-A")
]
result = center.findObservations(observer=observer1, notification="A", observable=observable1, identifier="identifier1-1-A")
self.assertEqual(result, expected)

if __name__ == "__main__":
unittest.main()

0 comments on commit 81006f0

Please sign in to comment.