From ed3db4f6415d44ebe9e4f0426a984c07805722c4 Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauld.mccarthy@gmail.com>
Date: Sun, 18 Sep 2016 19:50:48 +0100
Subject: [PATCH] Notifier allows enabling/disabling all listeners, or all
 listeners on a given topic.

---
 fsl/utils/notifier.py | 95 ++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 84 insertions(+), 11 deletions(-)

diff --git a/fsl/utils/notifier.py b/fsl/utils/notifier.py
index 54d055ab1..ba2d43d9f 100644
--- a/fsl/utils/notifier.py
+++ b/fsl/utils/notifier.py
@@ -85,9 +85,24 @@ class Notifier(object):
         """Initialises a dictionary of listeners on a new ``Notifier``
         instance.
         """
-        new             = object.__new__(cls)
+        
+        new = object.__new__(cls)
+
+        # Listeners are stored in this
+        #
+        # { topic : { name : _Listener } }
+        #
+        # dictionary, with the inner
+        # dictionaries ordered by
+        # insertion time.
         new.__listeners = collections.defaultdict(collections.OrderedDict)
 
+        # Notification can be enabled on a per-
+        # topic basis. This dictionary contains
+        # enable states for each topic, as
+        # { topic : enabled } mappings.
+        new.__enabled = {}
+
         if isinstance(new, props.HasProperties):
             log.warning('Warning: {} is a sub-class of both '
                         'Notifier and props.HasProperties!')
@@ -120,7 +135,9 @@ class Notifier(object):
             topic = DEFAULT_TOPIC
 
         listener = _Listener(name, callback, topic, runOnIdle)
+        
         self.__listeners[topic][name] = listener
+        self.__enabled[  topic]       = self.__enabled.get(topic, True)
 
         log.debug('{}: Registered {}'.format(type(self).__name__, listener))
 
@@ -154,25 +171,23 @@ class Notifier(object):
         # No more listeners for this topic
         if len(listeners) == 0:
             self.__listeners.pop(topic)
+            self.__enabled  .pop(topic)
         
         log.debug('{}: De-registered listener {}'.format(
             type(self).__name__, listener))
 
 
-    def enable(self, name, topic=None):
+    def enable(self, name, topic=None, enable=True):
         """Enables the specified listener. """
         if topic is None:
             topic = DEFAULT_TOPIC
 
-        self.__listeners[topic][name].enabled = True
+        self.__listeners[topic][name].enabled = enable
 
 
     def disable(self, name, topic=None):
         """Disables the specified listener. """
-        if topic is None:
-            topic = DEFAULT_TOPIC
-
-        self.__listeners[topic][name].enabled = False
+        self.enable(name, topic, False)
 
 
     def isEnabled(self, name, topic=None):
@@ -185,6 +200,55 @@ class Notifier(object):
         return self.__listeners[topic][name].enabled
 
 
+    def enableAll(self, topic=None, state=True):
+        """Enable/disable all listeners for the specified topic.
+
+        :arg topic: Topic to enable/disable listeners on. If ``None``,
+                    all listeners are enabled/disabled.
+
+        :arg state: State to set listeners to.
+        """
+
+
+        if topic is None:
+            topic = DEFAULT_TOPIC
+
+        self.__enabled[topic] = state
+
+    
+    def disableAll(self, topic=None):
+        """Disable all listeners for the specified topic (or ``None``
+        to disable all listeners).
+        """
+        self.enableAll(False)
+
+
+    def isAllEnabled(self, topic=None):
+        """Returns ``True`` if all listeners for the specified topic (or all
+        listeners if ``topic=None``) are enabled, ``False`` otherwise.
+        """
+        if topic is None:
+            topic = DEFAULT_TOPIC
+            
+        return self.__enabled[topic]
+
+
+    @contextlib.contextmanager
+    def skipAll(self, topic=None):
+        """Context manager which disables all listeners for the
+        specified, and restores their state before returning.
+        """
+        
+        state = self.isAllEnabled(topic)
+        
+        self.disableAll(topic)
+
+        try:
+            yield
+        finally:
+            self.enableAll(topic, state)
+
+
     @contextlib.contextmanager
     def skip(self, name, topic=None):
         """Context manager which disables the speciifed listener, and
@@ -236,11 +300,20 @@ class Notifier(object):
                   See :meth:`register`.
         """
 
-        topic     = kwargs.get('topic', DEFAULT_TOPIC)
-        value     = kwargs.get('value', None)
-        listeners = [self.__listeners[topic]]
+        topic        = kwargs.get('topic', DEFAULT_TOPIC)
+        value        = kwargs.get('value', None)
+        isDefault    = topic == DEFAULT_TOPIC
+        allEnabled   = self.__enabled.get(DEFAULT_TOPIC, True)
+        topicEnabled = ((isDefault and allEnabled) or
+                        self.__enabled.get(topic), True)
+                       
+        if not allEnabled:
+            return
+
+        if topicEnabled:
+            listeners = [self.__listeners[topic]]
 
-        if topic != DEFAULT_TOPIC:
+        if not isDefault:
             listeners.append(self.__listeners[DEFAULT_TOPIC])
 
         if sum(map(len, listeners)) == 0:
-- 
GitLab