Nuwan Goonasekera 7 лет назад
Родитель
Сommit
51c61dbdc2

+ 41 - 34
cloudbridge/cloud/base/events.py

@@ -1,5 +1,4 @@
 import bisect
-import collections
 import fnmatch
 import logging
 import re
@@ -13,9 +12,9 @@ log = logging.getLogger(__name__)
 
 class InterceptingEventHandler(EventHandler):
 
-    def __init__(self, event_name, priority, callback):
+    def __init__(self, event_pattern, priority, callback):
         self.__dispatcher = None
-        self.event_name = event_name
+        self.event_pattern = event_pattern
         self.priority = priority
         self.callback = callback
 
@@ -24,8 +23,8 @@ class InterceptingEventHandler(EventHandler):
         # event handlers sorted by priority
         return self.priority < other.priority
 
-    def _get_next_handler(self, full_event_name):
-        handler_list = self.dispatcher.handler_cache.get(full_event_name, [])
+    def _get_next_handler(self, event):
+        handler_list = self.dispatcher.get_handlers_for_event(event)
         # find position of this handler
         pos = bisect.bisect_left(handler_list, self)
         assert handler_list[pos] == self
@@ -34,6 +33,15 @@ class InterceptingEventHandler(EventHandler):
         else:
             return None
 
+    def event_pattern(self):
+        pass
+
+    def priority(self):
+        pass
+
+    def callback(self):
+        pass
+
     @property
     def dispatcher(self):
         return self.__dispatcher
@@ -45,7 +53,7 @@ class InterceptingEventHandler(EventHandler):
 
     def invoke(self, **kwargs):
         kwargs.pop('next_handler', None)
-        next_handler = self._get_next_handler(kwargs.get('event_name', None))
+        next_handler = self._get_next_handler(kwargs.get('event', None))
         # callback is responsible for invoking the next_handler and
         # controlling the result value
         return self.callback(next_handler=next_handler, **kwargs)
@@ -57,8 +65,8 @@ class InterceptingEventHandler(EventHandler):
 
 class ObservingEventHandler(InterceptingEventHandler):
 
-    def __init__(self, event_name, priority, callback):
-        super(ObservingEventHandler, self).__init__(event_name, priority,
+    def __init__(self, event_pattern, priority, callback):
+        super(ObservingEventHandler, self).__init__(event_pattern, priority,
                                                     callback)
 
     def invoke(self, **kwargs):
@@ -66,7 +74,7 @@ class ObservingEventHandler(InterceptingEventHandler):
         kwargs.pop('next_handler', None)
         self.callback(**kwargs)
         # Kick off the handler chain
-        next_handler = self._get_next_handler(kwargs.get('event_name', None))
+        next_handler = self._get_next_handler(kwargs.get('event', None))
         if next_handler:
             return next_handler.invoke(**kwargs)
         else:
@@ -76,21 +84,20 @@ class ObservingEventHandler(InterceptingEventHandler):
 class SimpleEventDispatcher(EventDispatcher):
 
     def __init__(self):
-        # The dict key is event_name.
-        # The dict value is a list of handlers for the event, sorted by event
-        # priority
-        self.__events = collections.OrderedDict({})
+        # The dict key is event_pattern.
+        # The dict value is a list of handlers for the event pattern, sorted
+        # by event priority
+        self.__events = {}
         self.__handler_cache = {}
 
-    @property
-    def handler_cache(self):
-        return self.__handler_cache
+    def get_handlers_for_event(self, event):
+        return self.__handler_cache.get(event, [])
 
-    def _create_handler_cache(self, event_name):
+    def _create_handler_cache(self, event):
         cache_list = []
-        # sort from most specific to least specific
+        # Find all patterns matching event
         for key in self.__events.keys():
-            if re.search(fnmatch.translate(key), event_name):
+            if re.search(fnmatch.translate(key), event):
                 cache_list.extend(self.__events[key])
         cache_list.sort(key=lambda h: h.priority)
 
@@ -112,48 +119,48 @@ class SimpleEventDispatcher(EventDispatcher):
                       "at priority '{}', with function names [{}]. " \
                       "Each priority must only have a single " \
                       "corresponding handler." \
-                .format(event_name, guilty_prio, ", ".join(guilty_names))
+                .format(event, guilty_prio, ", ".join(guilty_names))
             raise HandlerException(message)
         return cache_list
 
     def subscribe(self, event_handler):
         event_handler.dispatcher = self
-        handler_list = self.__events.get(event_handler.event_name, [])
+        handler_list = self.__events.get(event_handler.event_pattern, [])
         handler_list.append(event_handler)
-        self.__events[event_handler.event_name] = handler_list
+        self.__events[event_handler.event_pattern] = handler_list
         # invalidate cache
         self.__handler_cache = {}
 
     def unsubscribe(self, event_handler):
-        handler_list = self.__events.get(event_handler.event_name, [])
+        handler_list = self.__events.get(event_handler.event_pattern, [])
         handler_list.remove(event_handler)
         event_handler.dispatcher = None
         # invalidate cache
         self.__handler_cache = {}
 
-    def observe(self, event_name, priority, callback):
-        handler = ObservingEventHandler(event_name, priority, callback)
+    def observe(self, event_pattern, priority, callback):
+        handler = ObservingEventHandler(event_pattern, priority, callback)
         self.subscribe(handler)
         return handler
 
-    def intercept(self, event_name, priority, callback):
-        handler = InterceptingEventHandler(event_name, priority, callback)
+    def intercept(self, event_pattern, priority, callback):
+        handler = InterceptingEventHandler(event_pattern, priority, callback)
         self.subscribe(handler)
         return handler
 
-    def emit(self, sender, event_name, **kwargs):
-        handlers = self.handler_cache.get(event_name)
+    def emit(self, sender, event, **kwargs):
+        handlers = self.__handler_cache.get(event)
         if handlers is None:
-            self.__handler_cache[event_name] = self._create_handler_cache(
-                event_name)
-            handlers = self.handler_cache.get(event_name)
+            self.__handler_cache[event] = self._create_handler_cache(
+                event)
+            handlers = self.__handler_cache.get(event)
 
         if handlers:
             # only kick off first handler in chain
-            return handlers[0].invoke(sender=sender, event_name=event_name,
+            return handlers[0].invoke(sender=sender, event=event,
                                       **kwargs)
         else:
             message = "Event '{}' has no subscribed handlers.".\
-                format(event_name)
+                format(event)
             log.warning(message)
             return None

+ 13 - 13
cloudbridge/cloud/base/services.py

@@ -41,30 +41,30 @@ class BaseCloudService(CloudService):
 
     def __init__(self, provider):
         self._provider = provider
-        self._service_event_name = "provider"
+        self._service_event_pattern = "provider"
 
     @property
     def provider(self):
         return self._provider
 
-    def _generate_event_name(self, func_name):
-        return ".".join((self._service_event_name, func_name))
+    def _generate_event_pattern(self, func_name):
+        return ".".join((self._service_event_pattern, func_name))
 
     def observe(self, func_name, priority, callback):
-        event_name = self._generate_event_name(func_name)
-        self.provider.events.observe(event_name, priority, callback)
+        event_pattern = self._generate_event_pattern(func_name)
+        self.provider.events.observe(event_pattern, priority, callback)
 
     def check_initialized(self, func_name):
-        event_name = self._generate_event_name(func_name)
-        return self.provider.events.check_initialized(event_name)
+        event_pattern = self._generate_event_pattern(func_name)
+        return self.provider.events.check_initialized(event_pattern)
 
     def mark_initialized(self, func_name):
-        event_name = self._generate_event_name(func_name)
-        self.provider.events.mark_initialized(event_name)
+        event_pattern = self._generate_event_pattern(func_name)
+        self.provider.events.mark_initialized(event_pattern)
 
     def call(self, func_name, priority, callback, **kwargs):
-        event_name = self._generate_event_name(func_name)
-        return self.provider.events.call(event_name, priority, callback,
+        event_pattern = self._generate_event_pattern(func_name)
+        return self.provider.events.call(event_pattern, priority, callback,
                                          **kwargs)
 
 
@@ -145,7 +145,7 @@ class BaseBucketService(
 
     def __init__(self, provider):
         super(BaseBucketService, self).__init__(provider)
-        self._service_event_name = "provider.storage.buckets"
+        self._service_event_pattern = "provider.storage.buckets"
 
     def _init_get(self):
         def _get_pre_log(bucket_id):
@@ -264,7 +264,7 @@ class BaseBucketObjectService(
 
     def __init__(self, provider):
         super(BaseBucketObjectService, self).__init__(provider)
-        self._service_event_name = "provider.storage.bucket_objects"
+        self._service_event_pattern = "provider.storage.bucket_objects"
         self._bucket = None
 
     def set_bucket(self, bucket):

+ 63 - 12
cloudbridge/cloud/interfaces/events.py

@@ -1,4 +1,5 @@
 from abc import ABCMeta, abstractmethod
+from abc import abstractproperty
 
 
 class EventDispatcher(object):
@@ -6,7 +7,7 @@ class EventDispatcher(object):
     __metaclass__ = ABCMeta
 
     @abstractmethod
-    def observe(self, event_name, priority, callback):
+    def observe(self, event_pattern, priority, callback):
         """
         Register a callback to be invoked when a given event occurs. `observe`
         will allow you to listen to events as they occur, but not modify the
@@ -14,9 +15,10 @@ class EventDispatcher(object):
         the `intercept` method. `observe` is a simplified case of `intercept`,
         and receives a simpler list of parameters in its callback.
 
-        :type event_name: str
-        :param event_name: The name of the event to which you are subscribing
-            the callback function. Accepts wildcard parameters.
+        :type event_pattern: str
+        :param event_pattern: The name or pattern of the event to which you are
+            subscribing the callback function. The pattern may contain glob
+            wildcard parameters to be notified on any matching event name.
 
         :type priority: int
         :param priority: The priority that this handler should be given.
@@ -36,7 +38,7 @@ class EventDispatcher(object):
         pass
 
     @abstractmethod
-    def intercept(self, event_name, priority, callback):
+    def intercept(self, event_pattern, priority, callback):
         """
         Register a callback to be invoked when a given event occurs. Intercept
         will allow you to both observe events and modify the event chain and
@@ -45,9 +47,10 @@ class EventDispatcher(object):
         callback receives, with intercept receiving additional parameters to
         allow controlling the event chain.
 
-        :type event_name: str
-        :param event_name: The name of the event to which you are subscribing
-            the callback function. Accepts wildcard parameters.
+        :type event_pattern: str
+        :param event_pattern: The name or pattern of the event to which you are
+            subscribing the callback function. The pattern may contain glob
+            wildcard parameters to be notified on any matching event name.
 
         :type priority: int
         :param priority: The priority that this handler should be given.
@@ -67,12 +70,12 @@ class EventDispatcher(object):
         pass
 
     @abstractmethod
-    def emit(self, sender, event_name, **kwargs):
+    def emit(self, sender, event, **kwargs):
         """
         Raises an event while registering a given callback
 
-        :type event_name: str
-        :param event_name: The name of the event which is being raised.
+        :type event: str
+        :param event: The name of the event which is being raised.
 
         :type sender: object
         :param sender: The object which is raising the event
@@ -103,11 +106,59 @@ class EventDispatcher(object):
         """
         pass
 
+    @abstractmethod
+    def get_handlers_for_event(self, event):
+        """
+        Returns a list of all registered handlers for a given event, sorted
+        in order of priority.
+
+        :type event: str
+        :param event: The name of the event
+        """
+        pass
+
 
 class EventHandler(object):
 
     __metaclass__ = ABCMeta
 
+    @abstractproperty
+    def event_pattern(self):
+        """
+        The event pattern that this handler is listening to. May include glob
+        patterns, in which case, any matching event name will trigger this
+        handler.
+        e.g.
+            provider.storage.*
+            provider.storage.volumes.list
+        """
+        pass
+
+    @abstractproperty
+    def priority(self):
+        """
+        The priority of this handler. When a matching event occurs, handlers
+        are invoked in order of priority.
+        The priorities ranges from 0-1000 and 2000-3000 and >4000 are reserved
+        for use by cloudbridge.
+        Users should listen on priorities between 1000-2000 for pre handlers
+        and 2000-3000 for post handlers.
+        e.g.
+            provider.storage.*
+            provider.storage.volumes.list
+        """
+        pass
+
+    @abstractproperty
+    def callback(self):
+        """
+        The callback that will be triggered when this event handler is invoked.
+        The callback signature must accept **kwargs and pass them through.
+        In general, the callback will always receive the event that
+        triggered this handler as an argument.
+        """
+        pass
+
     @abstractmethod
     def invoke(self, **kwargs):
         """
@@ -122,7 +173,7 @@ class EventHandler(object):
         """
         pass
 
-    @abstractmethod
+    @abstractproperty
     def dispatcher(self):
         """
         Get or sets the dispatcher currently associated with this event handler

+ 13 - 11
test/test_event_system.py

@@ -20,12 +20,13 @@ class EventSystemTestCase(unittest.TestCase):
         def my_callback(**kwargs):
             self.assertDictEqual(kwargs,
                                  {'sender': self,
-                                  'event_name': EVENT_NAME})
+                                  'event': EVENT_NAME})
             callback_tracker[0] += 'obs'
             return "hello"
 
         dispatcher = SimpleEventDispatcher()
-        handler = dispatcher.observe(EVENT_NAME, 1000, my_callback)
+        handler = dispatcher.observe(event_pattern=EVENT_NAME, priority=1000,
+                                     callback=my_callback)
         self.assertIsInstance(handler, EventHandler)
         result = dispatcher.emit(self, EVENT_NAME)
         self.assertEqual(
@@ -42,13 +43,14 @@ class EventSystemTestCase(unittest.TestCase):
         def my_callback(**kwargs):
             self.assertDictEqual(kwargs,
                                  {'sender': self,
-                                  'event_name': EVENT_NAME,
+                                  'event': EVENT_NAME,
                                   'next_handler': None})
             callback_tracker[0] += "intcpt"
             return "world"
 
         dispatcher = SimpleEventDispatcher()
-        handler = dispatcher.intercept(EVENT_NAME, 1000, my_callback)
+        handler = dispatcher.intercept(event_pattern=EVENT_NAME, priority=1000,
+                                       callback=my_callback)
         self.assertIsInstance(handler, EventHandler)
         result = dispatcher.emit(self, EVENT_NAME)
         self.assertEqual(
@@ -65,14 +67,14 @@ class EventSystemTestCase(unittest.TestCase):
         def my_callback_obs(**kwargs):
             self.assertDictEqual(kwargs,
                                  {'sender': self,
-                                  'event_name': EVENT_NAME})
+                                  'event': EVENT_NAME})
             callback_tracker[0] += "obs_"
             return "hello"
 
         def my_callback_intcpt(**kwargs):
             self.assertDictEqual(kwargs,
                                  {'sender': self,
-                                  'event_name': EVENT_NAME,
+                                  'event': EVENT_NAME,
                                   'next_handler': None})
             callback_tracker[0] += "intcpt_"
             return "world"
@@ -107,7 +109,7 @@ class EventSystemTestCase(unittest.TestCase):
         def my_callback_obs(**kwargs):
             self.assertDictEqual(kwargs,
                                  {'sender': self,
-                                  'event_name': EVENT_NAME})
+                                  'event': EVENT_NAME})
             callback_tracker[0] += "obs_"
             return "hello"
 
@@ -139,7 +141,7 @@ class EventSystemTestCase(unittest.TestCase):
         def my_callback_intcpt2(**kwargs):
             self.assertDictEqual(kwargs,
                                  {'sender': self,
-                                  'event_name': EVENT_NAME,
+                                  'event': EVENT_NAME,
                                   'next_handler': None})
             callback_tracker[0] += "intcpt2_"
             return "world"
@@ -194,21 +196,21 @@ class EventSystemTestCase(unittest.TestCase):
 
         def my_callback1(**kwargs):
             self.assertDictEqual(kwargs, {'sender': self,
-                                          'event_name': EVENT_NAME})
+                                          'event': EVENT_NAME})
             callback_tracker[0] += "event1_"
             return "hello"
 
         def my_callback2(**kwargs):
             self.assertDictEqual(kwargs,
                                  {'sender': self,
-                                  'event_name': "event.hello.anotherworld"})
+                                  'event': "event.hello.anotherworld"})
             callback_tracker[0] += "event2_"
             return "another"
 
         def my_callback3(**kwargs):
             self.assertDictEqual(kwargs,
                                  {'sender': self,
-                                  'event_name': "event.hello.anotherworld",
+                                  'event': "event.hello.anotherworld",
                                   'next_handler': None})
             callback_tracker[0] += "event3_"
             return "world"