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

Extracted interface for events and refactored accordingly

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

+ 169 - 0
cloudbridge/cloud/base/events.py

@@ -0,0 +1,169 @@
+import fnmatch
+import re
+from copy import deepcopy
+from enum import Enum
+
+from ..interfaces.events import EventDispatcher
+from ..interfaces.exceptions import HandlerException
+
+
+class HandlerType(Enum):
+    """
+    Handler Types.
+    """
+    SUBSCRIPTION = 'subscription'
+    INTERCEPTION = 'intercept'
+
+
+class SimpleEventDispatcher(EventDispatcher):
+
+    def __init__(self):
+        self.__events = {}
+        self.__initialized = {}
+
+    def get_handlers(self, event_name):
+        return self.__events.get(event_name)
+
+    def check_initialized(self, event_name):
+        return self.__initialized.get(event_name) or False
+
+    def mark_initialized(self, event_name):
+        self.__initialized[event_name] = True
+
+    def subscribe(self, event_name, priority, 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, 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)
+        finally:
+            self.__events[event_name].remove((priority, handler))
+        return ret_obj
+
+    def _emit(self, event_name, **kwargs):
+
+        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
+            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.".\
+                format(event_name)
+            raise HandlerException(message)
+
+        prev_handler = None
+        first_handler = None
+        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)
+
+
+class EventHandler(object):
+    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):
+        return self._next_handler
+
+    @next_handler.setter
+    def next_handler(self, new_handler):
+        self._next_handler = new_handler
+
+    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)
+
+                if new_result:
+                    result = new_result
+
+            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

+ 5 - 176
cloudbridge/cloud/base/provider.py

@@ -1,10 +1,7 @@
 """Base implementation of a provider interface."""
 """Base implementation of a provider interface."""
-import fnmatch
 import functools
 import functools
 import logging
 import logging
 import os
 import os
-import re
-from copy import deepcopy
 from os.path import expanduser
 from os.path import expanduser
 try:
 try:
     from configparser import ConfigParser
     from configparser import ConfigParser
@@ -13,11 +10,10 @@ except ImportError:  # Python 2
 
 
 import six
 import six
 
 
-from cloudbridge.cloud.interfaces import CloudProvider
-from cloudbridge.cloud.interfaces.exceptions import HandlerException
-from cloudbridge.cloud.interfaces.exceptions import ProviderConnectionException
-from cloudbridge.cloud.interfaces.provider import HandlerType
-from cloudbridge.cloud.interfaces.resources import Configuration
+from ..base.events import SimpleEventDispatcher
+from ..interfaces import CloudProvider
+from ..interfaces.exceptions import ProviderConnectionException
+from ..interfaces.resources import Configuration
 
 
 log = logging.getLogger(__name__)
 log = logging.getLogger(__name__)
 
 
@@ -84,179 +80,12 @@ class BaseConfiguration(Configuration):
         return self.get('cb_debug', os.environ.get('CB_DEBUG', False))
         return self.get('cb_debug', os.environ.get('CB_DEBUG', False))
 
 
 
 
-class EventDispatcher(object):
-    def __init__(self):
-        self.__events = {}
-        self.__initialized = {}
-
-    def get_handlers(self, event_name):
-        return self.__events.get(event_name)
-
-    def check_initialized(self, event_name):
-        return self.__initialized.get(event_name) or False
-
-    def mark_initialized(self, event_name):
-        self.__initialized[event_name] = True
-
-    def subscribe(self, event_name, priority, callback):
-        """
-        Subscribe a handler by event name to the dispatcher.
-
-        :type event_name: str
-        :param event_name: The name of the event to which you are subscribing
-        the callback function.
-        :type priority: int
-        :param priority: The priority that this handler should be given.
-        When the event is emitted, all handlers will be run in order of
-        priority.
-        :type callback: function
-        :param callback: The callback function that should be called with
-        the parameters given at when the even is emitted.
-        """
-        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, 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)
-        finally:
-            self.__events[event_name].remove((priority, handler))
-        return ret_obj
-
-    def _emit(self, event_name, **kwargs):
-
-        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
-            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.".\
-                format(event_name)
-            raise HandlerException(message)
-
-        prev_handler = None
-        first_handler = None
-        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)
-
-
-class EventHandler(object):
-    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):
-        return self._next_handler
-
-    @next_handler.setter
-    def next_handler(self, new_handler):
-        self._next_handler = new_handler
-
-    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)
-
-                if new_result:
-                    result = new_result
-
-            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):
 class BaseCloudProvider(CloudProvider):
     def __init__(self, config):
     def __init__(self, config):
         self._config = BaseConfiguration(config)
         self._config = BaseConfiguration(config)
         self._config_parser = ConfigParser()
         self._config_parser = ConfigParser()
         self._config_parser.read(CloudBridgeConfigLocations)
         self._config_parser.read(CloudBridgeConfigLocations)
-        self._events = EventDispatcher()
+        self._events = SimpleEventDispatcher()
 
 
     @property
     @property
     def config(self):
     def config(self):

+ 75 - 0
cloudbridge/cloud/interfaces/events.py

@@ -0,0 +1,75 @@
+from abc import ABCMeta, abstractmethod
+
+
+class EventDispatcher(object):
+
+    __metaclass__ = ABCMeta
+
+    @abstractmethod
+    def subscribe(self, event_name, priority, callback):
+        """
+        Register a callback to be invoked when a given event occurs. Listen
+        will allow you to observe events as they occur, but not modify the
+        event chain or its parameters. If you need to modify an event, use
+        the intercept method. `listen` 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 priority: int
+        :param priority: The priority that this handler should be given.
+            When the event is emitted, all handlers will be run in order of
+            priority.
+
+        :type callback: function
+        :param callback: The callback function that should be called with
+            the parameters given at when the even is emitted.
+        """
+        pass
+
+    @abstractmethod
+    def intercept(self, event_name, 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
+        its parameters. If you only want to observe an event, use the listen
+        method. Intercept and listen only differ in what parameters the
+        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 priority: int
+        :param priority: The priority that this handler should be given.
+            When the event is emitted, all handlers will be run in order of
+            priority.
+
+        :type callback: function
+        :param callback: The callback function that should be called with
+            the parameters given at when the even is emitted.
+        """
+        pass
+
+    @abstractmethod
+    def call(self, event_name, priority, callback, **kwargs):
+        """
+        Raises an event while registering a given 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 priority: int
+        :param priority: The priority that this handler should be given.
+            When the event is emitted, all handlers will be run in order of
+            priority.
+
+        :type callback: function
+        :param callback: The callback function that should be called with
+            the parameters given at when the even is emitted.
+        """
+        pass

+ 0 - 9
cloudbridge/cloud/interfaces/provider.py

@@ -2,7 +2,6 @@
 Specification for a provider interface
 Specification for a provider interface
 """
 """
 from abc import ABCMeta, abstractmethod, abstractproperty
 from abc import ABCMeta, abstractmethod, abstractproperty
-from enum import Enum
 
 
 
 
 class CloudProvider(object):
 class CloudProvider(object):
@@ -246,11 +245,3 @@ class DeploymentProvider(object):
         Deploys on given target, where target is an Instance or Container
         Deploys on given target, where target is an Instance or Container
         """
         """
         pass
         pass
-
-
-class HandlerType(Enum):
-    """
-    Handler Types.
-    """
-    SUBSCRIPTION = 'subscription'
-    INTERCEPTION = 'intercept'