Просмотр исходного кода

Wildcard matching & Interception

almahmoud 7 лет назад
Родитель
Сommit
e1fc1e26cb
2 измененных файлов с 92 добавлено и 42 удалено
  1. 90 41
      cloudbridge/cloud/base/provider.py
  2. 2 1
      cloudbridge/cloud/interfaces/provider.py

+ 90 - 41
cloudbridge/cloud/base/provider.py

@@ -1,7 +1,10 @@
 """Base implementation of a provider interface."""
+import fnmatch
 import functools
 import logging
 import os
+import re
+from copy import deepcopy
 from os.path import expanduser
 try:
     from configparser import ConfigParser
@@ -95,7 +98,7 @@ class EventDispatcher(object):
     def mark_initialized(self, event_name):
         self.__initialized[event_name] = True
 
-    def subscribe(self, event_name, priority, callback, result_callback=False):
+    def subscribe(self, event_name, priority, callback):
         """
         Subscribe a handler by event name to the dispatcher.
 
@@ -109,24 +112,24 @@ class EventDispatcher(object):
         :type callback: function
         :param callback: The callback function that should be called with
         the parameters given at when the even is emitted.
-        :type result_callback: bool
-        :param result_callback: Whether the callback function should be using
-        the last return value from previous functions as an argument. This
-        function should accept a `callback_result` parameter in addition to
-        the parameters of the event.
         """
-        if result_callback:
-            handler = EventHandler(HandlerType.RESULT_SUBSCRIPTION, callback)
-        else:
-            handler = EventHandler(HandlerType.SUBSCRIPTION, callback)
+        handler = EventHandler(HandlerType.SUBSCRIPTION, callback, priority)
+        if not self.__events.get(event_name):
+            self.__events[event_name] = list()
+        self.__events[event_name].append((priority, handler))
+
+    def intercept(self, event_name, priority, callback):
+        handler = EventHandler(HandlerType.INTERCEPTION, callback, priority)
         if not self.__events.get(event_name):
             self.__events[event_name] = list()
         self.__events[event_name].append((priority, handler))
 
     def call(self, event_name, priority, callback, **kwargs):
-        handler = EventHandler(HandlerType.SUBSCRIPTION, callback)
+        handler = EventHandler(HandlerType.SUBSCRIPTION, callback, priority)
         if not self.__events.get(event_name):
             self.__events[event_name] = list()
+        # Although handler object has priority property, keep it a pair to not
+        # access each handler when sorting
         self.__events[event_name].append((priority, handler))
         try:
             ret_obj = self._emit(event_name, **kwargs)
@@ -136,18 +139,36 @@ class EventDispatcher(object):
 
     def _emit(self, event_name, **kwargs):
 
-        def priority_sort(handler_list):
-            handler_list.sort(key=lambda x: x[0])
+        def _match_and_sort(event_name):
+            new_list = []
+            for key in self.__events.keys():
+                if re.search(fnmatch.translate(key), event_name):
+                    new_list.extend(deepcopy(self.__events[key]))
+            new_list.sort(key=lambda x: x[0])
             # Make sure all priorities are unique
-            prev_prio = None
-            for (priority, handler) in handler_list:
-                if priority == prev_prio:
-                    message = "Event '{}' has multiple subscribed handlers " \
-                              "at priority '{}'. Each priority must only " \
-                              "have a single corresponding handler."\
-                        .format(event_name, priority)
-                    raise HandlerException(message)
-            return handler_list
+            priority_list = [x[0] for x in new_list]
+            if len(set(priority_list)) != len(priority_list):
+
+                guilty_prio = None
+                for prio in priority_list:
+                    if prio == guilty_prio:
+                        break
+                    guilty_prio = prio
+
+                # guilty_prio should never be none since we checked for
+                # duplicates before iterating
+                guilty_names = [x[1].callback.__name__
+                                        for x in new_list
+                                        if x[0] == guilty_prio]
+
+                message = "Event '{}' has multiple subscribed handlers " \
+                          "at priority '{}', with function names [{}]. " \
+                          "Each priority must only have a single " \
+                          "corresponding handler." \
+                    .format(event_name, priority, ", ".join(guilty_names))
+                raise HandlerException(message)
+
+            return new_list
 
         if not self.__events.get(event_name):
             message = "Event '{}' has no subscribed handlers.".\
@@ -156,20 +177,21 @@ class EventDispatcher(object):
 
         prev_handler = None
         first_handler = None
-        for (priority, handler) in priority_sort(self.__events[event_name]):
+        for (priority, handler) in _match_and_sort(event_name):
             if not first_handler:
                 first_handler = handler
             if prev_handler:
                 prev_handler.next_handler = handler
             prev_handler = handler
-        return first_handler.invoke(kwargs)
+        return first_handler.invoke(**kwargs)
 
 
 class EventHandler(object):
-    def __init__(self, handler_type, callback):
+    def __init__(self, handler_type, callback, priority):
         self.handler_type = handler_type
         self.callback = callback
         self._next_handler = None
+        self.priority = priority
 
     @property
     def next_handler(self):
@@ -179,28 +201,55 @@ class EventHandler(object):
     def next_handler(self, new_handler):
         self._next_handler = new_handler
 
-    def invoke(self, args):
-        if self.handler_type in [HandlerType.SUBSCRIPTION,
-                                 HandlerType.RESULT_SUBSCRIPTION]:
-            result = self.callback(**args)
+    def invoke(self, **kwargs):
+        if self.handler_type == HandlerType.SUBSCRIPTION:
+            result = self.callback(**kwargs)
+
+            next = self.next_handler
+            if next:
+                if next.handler_type == HandlerType.SUBSCRIPTION:
+                    if result or not kwargs.get('callback_result', None):
+                        kwargs['callback_result'] = result
+                    new_result = next.invoke(**kwargs)
+                elif next.handler_type == HandlerType.INTERCEPTION:
+                    new_result = next.invoke(**kwargs)
 
-        next = self.next_handler
-        if next:
-            if next.handler_type == HandlerType.RESULT_SUBSCRIPTION:
-                args['callback_result'] = result
-                new_result = next.invoke(args)
-                if new_result:
-                    result = new_result
-            elif next.handler_type == HandlerType.SUBSCRIPTION:
-                if args.get('callback_result'):
-                    args.pop('callback_result')
-                new_result = next.invoke(args)
                 if new_result:
                     result = new_result
 
-        self.next_handler = None
+            self.next_handler = None
+
+        elif self.handler_type == HandlerType.INTERCEPTION:
+            kwargs.pop('next_handler', None)
+            result = self.callback(next_handler=self.next_handler, **kwargs)
+            self.next_handler = None
+
         return result
 
+    def skip(self, **kwargs):
+        if self.next_handler:
+            self.next_handler.invoke(**kwargs)
+            self.next_handler = None
+
+    def skip_to_name(self, function_name, **kwargs):
+        if self.callback.__name__ == function_name:
+            self.invoke(**kwargs)
+        elif self.next_handler:
+            self.next_handler.skip_to_name(function_name, **kwargs)
+            self.next_handler = None
+
+    def skip_to_priority(self, priority, **kwargs):
+        if self.priority == priority:
+            self.invoke(**kwargs)
+        elif self.next_handler:
+            self.next_handler.skip_to_priority(priority, **kwargs)
+            self.next_handler = None
+
+    def skip_rest(self):
+        if self.next_handler:
+            self.next_handler.skip_rest()
+            self.next_handler = None
+
 
 class BaseCloudProvider(CloudProvider):
     def __init__(self, config):

+ 2 - 1
cloudbridge/cloud/interfaces/provider.py

@@ -253,4 +253,5 @@ class HandlerType(Enum):
     Handler Types.
     """
     SUBSCRIPTION = 'subscription'
-    RESULT_SUBSCRIPTION = 'result_subscription'
+    RESULT_SUBSCRIPTION = 'result_subscription'
+    INTERCEPTION = 'intercept'