소스 검색

Merge pull request #187 from CloudVE/eventsystem

Updated to use pyeventsystem
Nuwan Goonasekera 7 년 전
부모
커밋
55c32da159

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

@@ -1,226 +0,0 @@
-import bisect
-import fnmatch
-import logging
-import re
-
-from ..interfaces.events import EventDispatcher
-from ..interfaces.events import EventHandler
-from ..interfaces.exceptions import HandlerException
-
-log = logging.getLogger(__name__)
-
-
-class PlaceHoldingEventHandler(object):
-    def __init__(self, event_pattern, priority, callback, handler_class):
-        self.event_pattern = event_pattern
-        self.priority = priority
-        self.callback = callback
-        self.handler_class = handler_class
-
-
-class BaseEventHandler(EventHandler):
-
-    def __init__(self, event_pattern, priority, callback):
-        self.__dispatcher = None
-        self.__event_pattern = event_pattern
-        self.__priority = priority
-        self.__callback = callback
-
-    def __lt__(self, other):
-        # This is required for the bisect module to insert
-        # event handlers sorted by priority
-        return self.priority < other.priority
-
-    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
-        if pos < len(handler_list)-1:
-            return handler_list[pos+1]
-        else:
-            return None
-
-    @property
-    def event_pattern(self):
-        return self.__event_pattern
-
-    @property
-    def priority(self):
-        return self.__priority
-
-    @property
-    def callback(self):
-        return self.__callback
-
-    @callback.setter
-    def callback(self, value):
-        self.__callback = value
-
-    @property
-    def dispatcher(self):
-        return self.__dispatcher
-
-    @dispatcher.setter
-    # pylint:disable=arguments-differ
-    def dispatcher(self, value):
-        self.__dispatcher = value
-
-    def unsubscribe(self):
-        if self.dispatcher:
-            self.dispatcher.unsubscribe(self)
-
-
-class InterceptingEventHandler(BaseEventHandler):
-
-    def __init__(self, event_pattern, priority, callback):
-        super(InterceptingEventHandler, self).__init__(event_pattern, priority,
-                                                       callback)
-
-    def invoke(self, event_args, *args, **kwargs):
-        next_handler = self._get_next_handler(event_args.get('event'))
-        event_args['next_handler'] = next_handler
-        # callback is responsible for invoking the next_handler and
-        # controlling the result value
-        result = self.callback(event_args, *args, **kwargs)
-        # Remove handler specific callback info
-        event_args.pop('next_handler', None)
-        return result
-
-
-class ObservingEventHandler(BaseEventHandler):
-
-    def __init__(self, event_pattern, priority, callback):
-        super(ObservingEventHandler, self).__init__(event_pattern, priority,
-                                                    callback)
-
-    def invoke(self, event_args, *args, **kwargs):
-        # Observers shouldn't pass a next_handler
-        event_args.pop('next_handler', None)
-        next_handler = self._get_next_handler(event_args.get('event'))
-        # Notify listener. Ignore result from observable handler
-        self.callback(event_args, *args, **kwargs)
-        # Kick off the remaining handler chain
-        if next_handler:
-            return next_handler.invoke(event_args, *args, **kwargs)
-        else:
-            return None
-
-
-class ImplementingEventHandler(BaseEventHandler):
-
-    def __init__(self, event_pattern, priority, callback):
-        super(ImplementingEventHandler, self).__init__(event_pattern, priority,
-                                                       callback)
-
-    def invoke(self, event_args, *args, **kwargs):
-        result = self.callback(*args, **kwargs)
-        next_handler = self._get_next_handler(event_args.get('event'))
-        if next_handler:
-            event_args['next_handler'] = next_handler
-            event_args['result'] = result
-            next_handler.invoke(event_args, *args, **kwargs)
-            event_args.pop('result', None)
-            event_args.pop('next_handler', None)
-        return result
-
-
-class SimpleEventDispatcher(EventDispatcher):
-
-    def __init__(self):
-        # 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 = {}
-
-    def get_handlers_for_event(self, event):
-        handlers = self.__handler_cache.get(event)
-        if handlers is None:
-            self.__handler_cache[event] = self._create_handler_cache(
-                event)
-            return self.__handler_cache.get(event)
-        else:
-            return handlers
-
-    def _create_handler_cache(self, event):
-        cache_list = []
-        # Find all patterns matching event
-        for key in self.__events.keys():
-            if re.search(fnmatch.translate(key), event):
-                cache_list.extend(self.__events[key])
-        cache_list.sort(key=lambda h: h.priority)
-
-        # Make sure all priorities are unique
-        priority_list = [h.priority for h in cache_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 = [h.callback.__name__ for h in cache_list
-                            if h.priority == guilty_prio]
-
-            message = "Event '{}' has multiple subscribed handlers " \
-                      "at priority '{}', with function names [{}]. " \
-                      "Each priority must only have a single " \
-                      "corresponding handler." \
-                .format(event, guilty_prio, ", ".join(guilty_names))
-            raise HandlerException(message)
-        return cache_list
-
-    def _invalidate_cache(self, event_pattern=None):
-        if not event_pattern:
-            # invalidate entire cache
-            self.__handler_cache = {}
-        else:
-            # Smarter invalidation by only deleting events that
-            # are affected by the pattern
-            for key in list(self.__handler_cache.keys()):
-                if re.search(fnmatch.translate(event_pattern), key):
-                    del self.__handler_cache[key]
-
-    def subscribe(self, event_handler):
-        event_handler.dispatcher = self
-        handler_list = self.__events.get(event_handler.event_pattern, [])
-        handler_list.append(event_handler)
-        self.__events[event_handler.event_pattern] = handler_list
-        self._invalidate_cache(event_pattern=event_handler.event_pattern)
-
-    def unsubscribe(self, event_handler):
-        handler_list = self.__events.get(event_handler.event_pattern, [])
-        handler_list.remove(event_handler)
-        event_handler.dispatcher = None
-        self._invalidate_cache(event_pattern=event_handler.event_pattern)
-
-    def observe(self, event_pattern, priority, callback):
-        handler = ObservingEventHandler(event_pattern, priority, callback)
-        self.subscribe(handler)
-        return handler
-
-    def intercept(self, event_pattern, priority, callback):
-        handler = InterceptingEventHandler(event_pattern, priority, callback)
-        self.subscribe(handler)
-        return handler
-
-    def implement(self, event_pattern, priority, callback):
-        handler = ImplementingEventHandler(event_pattern, priority, callback)
-        self.subscribe(handler)
-        return handler
-
-    def dispatch(self, sender, event, *args, **kwargs):
-        handlers = self.get_handlers_for_event(event)
-
-        if handlers:
-            # only kick off first handler in chain
-            event_args = {'event': event, 'sender': sender}
-            return handlers[0].invoke(event_args, *args, **kwargs)
-        else:
-            message = "Event '{}' has no subscribed handlers.".\
-                format(event)
-            log.warning(message)
-            return None

+ 7 - 165
cloudbridge/cloud/base/middleware.py

@@ -1,179 +1,21 @@
-import functools
-import inspect
 import logging
 import logging
 import sys
 import sys
 
 
+from pyeventsystem.middleware import dispatch as pyevent_dispatch
+from pyeventsystem.middleware import intercept
+from pyeventsystem.middleware import observe
+
 import six
 import six
 
 
-from ..base.events import ImplementingEventHandler
-from ..base.events import InterceptingEventHandler
-from ..base.events import ObservingEventHandler
-from ..base.events import PlaceHoldingEventHandler
 from ..interfaces.exceptions import CloudBridgeBaseException
 from ..interfaces.exceptions import CloudBridgeBaseException
-from ..interfaces.exceptions import HandlerException
-from ..interfaces.middleware import Middleware
-from ..interfaces.middleware import MiddlewareManager
 
 
 log = logging.getLogger(__name__)
 log = logging.getLogger(__name__)
 
 
 
 
-def intercept(event_pattern, priority):
-    def deco(f):
-        # Mark function as having an event_handler so we can discover it
-        # The callback cannot be set to f as it is not bound yet and will be
-        # set during auto discovery
-        f.__event_handler = PlaceHoldingEventHandler(
-            event_pattern, priority, f, InterceptingEventHandler)
-        return f
-    return deco
-
-
-def observe(event_pattern, priority):
-    def deco(f):
-        # Mark function as having an event_handler so we can discover it
-        # The callback cannot be set to f as it is not bound yet and will be
-        # set during auto discovery
-        f.__event_handler = PlaceHoldingEventHandler(
-            event_pattern, priority, f, ObservingEventHandler)
-        return f
-    return deco
-
-
-def implement(event_pattern, priority):
-    def deco(f):
-        # Mark function as having an event_handler so we can discover it
-        # The callback will be unbound since we do not have access to `self`
-        # yet, and must be bound before invocation. This binding is done
-        # during middleware auto discovery
-        f.__event_handler = PlaceHoldingEventHandler(
-            event_pattern, priority, f, ImplementingEventHandler)
-        return f
-    return deco
-
-
-def dispatch(event, priority):
-    """
-    The event decorator combines the functionality of the implement decorator
-    and a manual event dispatch into a single decorator.
-    """
-    def deco(f):
-        @functools.wraps(f)
-        def wrapper(self, *args, **kwargs):
-            events = getattr(self, 'events', None)
-            if events:
-                # Don't call the wrapped method, just dispatch the event,
-                # and the event handler will get invoked
-                return events.dispatch(self, event, *args, **kwargs)
-            else:
-                raise HandlerException(
-                    "Cannot dispatch event: {0}. The object {1} should have"
-                    " an events property".format(event, self))
-        # Mark function as having an event_handler so we can discover it
-        # The callback f is unbound and will be bound during middleware
-        # auto discovery
-        wrapper.__event_handler = PlaceHoldingEventHandler(
-            event, priority, f, ImplementingEventHandler)
-        return wrapper
-    return deco
-
-
-class SimpleMiddlewareManager(MiddlewareManager):
-
-    def __init__(self, event_manager):
-        self.events = event_manager
-        self.middleware_list = []
-
-    def add(self, middleware):
-        if isinstance(middleware, Middleware):
-            m = middleware
-        else:
-            m = AutoDiscoveredMiddleware(middleware)
-        m.install(self.events)
-        self.middleware_list.append(m)
-        return m
-
-    def remove(self, middleware):
-        middleware.uninstall()
-        self.middleware_list.remove(middleware)
-
-
-class BaseMiddleware(Middleware):
-
-    def __init__(self):
-        self.event_handlers = []
-        self.events = None
-
-    def install(self, event_manager):
-        self.events = event_manager
-        discovered_handlers = self.discover_handlers(self)
-        self.add_handlers(discovered_handlers)
-
-    def add_handlers(self, handlers):
-        if not hasattr(self, "event_handlers"):
-            # In case the user forgot to call super class init
-            self.event_handlers = []
-        for handler in handlers:
-            self.events.subscribe(handler)
-        self.event_handlers.extend(handlers)
-
-    def uninstall(self):
-        for handler in self.event_handlers:
-            handler.unsubscribe()
-        self.event_handlers = []
-        self.events = None
-
-    @staticmethod
-    def discover_handlers(class_or_obj):
-
-        # https://bugs.python.org/issue30533
-        # simulating a getmembers_static to be easily replaced with the
-        # function if they add it to inspect module
-        def getmembers_static(obj, predicate=None):
-            results = []
-            for key in dir(obj):
-                if not inspect.isdatadescriptor(getattr(obj.__class__,
-                                                        key,
-                                                        None)):
-                    try:
-                        value = getattr(obj, key)
-                    except AttributeError:
-                        continue
-                    if not predicate or predicate(value):
-                        results.append((key, value))
-            return results
-
-        discovered_handlers = []
-        for _, func in getmembers_static(class_or_obj, inspect.ismethod):
-            handler = getattr(func, "__event_handler", None)
-            if handler and isinstance(handler, PlaceHoldingEventHandler):
-                # create a new handler that mimics the original one,
-                # essentially deep-copying the handler, so that the bound
-                # method is never stored in the function itself, preventing
-                # further bonding
-                new_handler = handler.handler_class(handler.event_pattern,
-                                                    handler.priority,
-                                                    handler.callback)
-                # Bind the currently unbound method
-                # and set the bound method as the callback
-                new_handler.callback = (new_handler.callback
-                                                   .__get__(class_or_obj))
-                discovered_handlers.append(new_handler)
-        return discovered_handlers
-
-
-class AutoDiscoveredMiddleware(BaseMiddleware):
-
-    def __init__(self, class_or_obj):
-        super(AutoDiscoveredMiddleware, self).__init__()
-        self.obj_to_discover = class_or_obj
-
-    def install(self, event_manager):
-        super(AutoDiscoveredMiddleware, self).install(event_manager)
-        discovered_handlers = self.discover_handlers(self.obj_to_discover)
-        self.add_handlers(discovered_handlers)
+dispatch = pyevent_dispatch
 
 
 
 
-class EventDebugLoggingMiddleware(BaseMiddleware):
+class EventDebugLoggingMiddleware(object):
     """
     """
     Logs all event parameters. This middleware should not be enabled other
     Logs all event parameters. This middleware should not be enabled other
     than for debugging, as it could log sensitive parameters such as
     than for debugging, as it could log sensitive parameters such as
@@ -190,7 +32,7 @@ class EventDebugLoggingMiddleware(BaseMiddleware):
             event_args.get("event"), event_args.get("result")))
             event_args.get("event"), event_args.get("result")))
 
 
 
 
-class ExceptionWrappingMiddleware(BaseMiddleware):
+class ExceptionWrappingMiddleware(object):
     """
     """
     Wraps all unhandled exceptions in cloudbridge exceptions.
     Wraps all unhandled exceptions in cloudbridge exceptions.
     """
     """

+ 3 - 8
cloudbridge/cloud/base/provider.py

@@ -8,11 +8,11 @@ try:
 except ImportError:  # Python 2
 except ImportError:  # Python 2
     from ConfigParser import SafeConfigParser as ConfigParser
     from ConfigParser import SafeConfigParser as ConfigParser
 
 
+from pyeventsystem.middleware import SimpleMiddlewareManager
+
 import six
 import six
 
 
-from ..base.events import SimpleEventDispatcher
 from ..base.middleware import ExceptionWrappingMiddleware
 from ..base.middleware import ExceptionWrappingMiddleware
-from ..base.middleware import SimpleMiddlewareManager
 from ..interfaces import CloudProvider
 from ..interfaces import CloudProvider
 from ..interfaces.exceptions import ProviderConnectionException
 from ..interfaces.exceptions import ProviderConnectionException
 from ..interfaces.resources import Configuration
 from ..interfaces.resources import Configuration
@@ -87,8 +87,7 @@ class BaseCloudProvider(CloudProvider):
         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 = SimpleEventDispatcher()
-        self._middleware = SimpleMiddlewareManager(self._events)
+        self._middleware = SimpleMiddlewareManager()
         self.add_required_middleware()
         self.add_required_middleware()
 
 
     @property
     @property
@@ -99,10 +98,6 @@ class BaseCloudProvider(CloudProvider):
     def name(self):
     def name(self):
         return str(self.__class__.__name__)
         return str(self.__class__.__name__)
 
 
-    @property
-    def events(self):
-        return self._events
-
     @property
     @property
     def middleware(self):
     def middleware(self):
         return self._middleware
         return self._middleware

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

@@ -54,7 +54,7 @@ class BaseCloudService(CloudService):
 
 
     @property
     @property
     def events(self):
     def events(self):
-        return self._provider.events
+        return self._provider.middleware.events
 
 
 
 
 class BaseSecurityService(SecurityService, BaseCloudService):
 class BaseSecurityService(SecurityService, BaseCloudService):

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

@@ -1,235 +0,0 @@
-from abc import ABCMeta
-from abc import abstractmethod
-from abc import abstractproperty
-
-
-class EventDispatcher(object):
-
-    __metaclass__ = ABCMeta
-
-    @abstractmethod
-    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
-        event chain or its parameters. If you need to modify an event, use
-        the `intercept` method. `observe` is a simplified case of `intercept`,
-        and receives a simpler list of parameters in its callback.
-
-        :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.
-            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 invoked. The
-            callback must have a signature of the form:
-            `def callback(event_args, *args, **kwargs)`
-            Where the first positional argument is always event_args, which
-            is a dict value containing information about the event.
-            `event_args` includes the following keys:
-            'event': The name of the event
-            'sender': The object which raised the event
-            The event_args dict can also be used to convey additional info to
-            downstream event handlers.
-            The rest of the arguments to the callback can be any combination
-            of positional or keyword arguments as desired.
-
-        :rtype: :class:`.EventHandler`
-        :return:  An object of class EventHandler. The EventHandler will
-            already be subscribed to the dispatcher, and need not be manually
-            subscribed. The returned event handler can be used to unsubscribe
-            from future events when required.
-        """
-        pass
-
-    @abstractmethod
-    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
-        its parameters. If you only want to observe an event, use the `observe`
-        method. Intercept and `observe` only differ in what parameters the
-        callback receives, with intercept receiving additional parameters to
-        allow controlling the event chain.
-
-        :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.
-            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 invoked. The
-            callback must have a signature of the form:
-            `def callback(event_args, *args, **kwargs)`
-            Where the first positional argument is always event_args, which
-            is a dict value containing information about the event.
-            `event_args` includes the following keys:
-            'event': The name of the event
-            'sender': The object which raised the event
-            'event_handler': The next event handler in the chain
-                             (only for intercepting handlers)
-            The event_args dict can also be used to convey additional info to
-            downstream event handlers.
-            The rest of the arguments to the callback can be any combination
-            of positional or keyword arguments as desired.
-
-        :rtype: :class:`.EventHandler`
-        :return:  An object of class EventHandler. The EventHandler will
-            already be subscribed to the dispatcher, and need not be manually
-            subscribed. The returned event handler can be used to unsubscribe
-            from future events when required.
-        """
-        pass
-
-    @abstractmethod
-    def dispatch(self, sender, event, *args, **kwargs):
-        """
-        Raises an event, which will trigger all handlers that are registered
-        for this event. The return value of the emit function is the return
-        value of the highest priority handler (i.e. the first handler in the
-        event chain). The first event handlers is responsible for calling the
-        next handler in the event chain, and so on, propagating arguments
-        and return values as desired.
-
-        :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
-
-        All additional positional and keyword arguments are passed through
-        to the callback functions for the event as is. Refer to the c
-        """
-        pass
-
-    @abstractmethod
-    def subscribe(self, event_handler):
-        """
-        Register an event handler with this dispatcher. The observe and
-        intercept methods will construct an event handler and subscribe it for
-        you automatically, and therefore, there is usually no need to invoke
-        subscribe directly unless you have a special type of event handler.
-
-        :type event_handler: :class:`.EventHandler`
-        :param event_handler: An object of class EventHandler.
-        """
-        pass
-
-    @abstractmethod
-    def unsubscribe(self, event_handler):
-        """
-        Unregister an event handler from this dispatcher. The event handler
-        will no longer be notified on events.
-
-        :type event_handler: :class:`.EventHandler`
-        :param event_handler: An object of class EventHandler.
-        """
-        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 *args and **kwargs and pass them
-        through.
-        The callback must have a signature of the form:
-        `def callback(event_args, *args, **kwargs)`
-        where the first positional argument is always event_args, which
-        is a dict value containing information about the event.
-        `event_args` includes the following keys:
-        'event': The name of the event
-        'sender': The object which raised the event
-        'event_handler': The next event handler in the chain
-                         (only for intercepting handlers)
-        The event_args dict can also be used to convey additional info to
-        downstream event handlers.
-        The rest of the arguments to the callback can be any combination
-        of positional or keyword arguments as desired.
-        """
-        pass
-
-    @abstractmethod
-    def invoke(self, event_args, *args, **kwargs):
-        """
-        Executes this event handler's callback
-
-        :type event_args: dict
-        :param event_args: The first positional argument is always event_args,
-           which is a dict value containing information about the event.
-           `event_args` includes the following keys:
-           'event': The name of the event
-           'sender': The object which raised the event
-           'event_handler': The next event handler in the chain
-                            (only for intercepting handlers)
-           The event_args dict can also be used to convey additional info to
-           downstream event handlers.
-        """
-        pass
-
-    @abstractmethod
-    def unsubscribe(self):
-        """
-        Unsubscribes from currently subscribed events.
-        """
-        pass
-
-    @abstractproperty
-    def dispatcher(self):
-        """
-        Get or sets the dispatcher currently associated with this event handler
-        """
-        pass

+ 0 - 8
cloudbridge/cloud/interfaces/exceptions.py

@@ -102,11 +102,3 @@ class InvalidParamException(InvalidNameException):
 
 
     def __init__(self, msg):
     def __init__(self, msg):
         super(InvalidParamException, self).__init__(msg)
         super(InvalidParamException, self).__init__(msg)
-
-
-class HandlerException(CloudBridgeBaseException):
-    """
-    Marker interface for event handler exceptions.
-    """
-    def __init__(self, msg):
-        super(HandlerException, self).__init__(msg)

+ 0 - 62
cloudbridge/cloud/interfaces/middleware.py

@@ -1,62 +0,0 @@
-from abc import ABCMeta
-from abc import abstractmethod
-
-
-class Middleware(object):
-    """
-    Provides a mechanism for grouping related event handlers together, to
-    provide logically cohesive middleware. The middleware class allows event
-    handlers to subscribe to events through the install method, and unsubscribe
-    through the uninstall method. This allows event handlers to be added and
-    removed as a group. The event handler implementations will also typically
-    live inside the middleware class. For example, LoggingMiddleware may
-    register multiple event handlers to log data before and after calls.
-    ResourceTrackingMiddleware may track all objects that are created or
-    deleted.
-    """
-
-    __metaclass__ = ABCMeta
-
-    @abstractmethod
-    def install(self, provider):
-        """
-        Use this method to subscribe all event handlers that are part of this
-        middleware. The install method will be called when the middleware is
-        first added to a MiddleWareManager.
-
-        :type provider: :class:`.Provider`
-        :param provider: The provider that this middleware belongs to
-        """
-        pass
-
-    @abstractmethod
-    def uninstall(self, provider):
-        """
-        Use this method to unsubscribe all event handlers for this middleware.
-        """
-        pass
-
-
-class MiddlewareManager(object):
-    """
-    Provides a mechanism for tracking a list of installed middleware
-    """
-
-    __metaclass__ = ABCMeta
-
-    @abstractmethod
-    def add(self, middleware):
-        """
-        Use this method to add middleware to this middleware manager.
-
-        :type middleware: :class:`.Middleware`
-        :param middleware: The middleware implementation
-        """
-        pass
-
-    @abstractmethod
-    def remove(self, middleware):
-        """
-        Use this method to remove this middleware from the middleware manager.
-        """
-        pass

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

@@ -55,6 +55,21 @@ class CloudProvider(object):
                   used to initialize the provider, as well as other global
                   used to initialize the provider, as well as other global
                   configuration properties.
                   configuration properties.
         """
         """
+        pass
+
+    @abstractproperty
+    def middleware(self):
+        """
+        Returns the middleware manager associated with this provider. The
+        middleware manager can be used to add or remove middleware from
+        cloudbridge. Refer to pyeventsystem documentation for more information
+        on how the middleware manager works.
+
+        :rtype: :class:`.MiddlewareManager`
+        :return:  An object of class MiddlewareManager, which can be used to
+        add or remove middleware from cloudbridge.
+        """
+        pass
 
 
     @abstractmethod
     @abstractmethod
     def authenticate(self):
     def authenticate(self):

+ 2 - 0
requirements.txt

@@ -1 +1,3 @@
+# TODO: move to setup.py when pyeventsystem is released to pip
+git+https://github.com/CloudVE/pyeventsystem
 -e ".[dev]"
 -e ".[dev]"

+ 0 - 471
test/test_event_system.py

@@ -1,471 +0,0 @@
-import unittest
-
-from cloudbridge.cloud.base.events import SimpleEventDispatcher
-from cloudbridge.cloud.interfaces.events import EventHandler
-from cloudbridge.cloud.interfaces.exceptions import HandlerException
-
-
-class EventSystemTestCase(unittest.TestCase):
-
-    def test_emit_event_no_handlers(self):
-        dispatcher = SimpleEventDispatcher()
-        result = dispatcher.dispatch(self, "event.hello.world")
-        self.assertIsNone(result, "Result should be none as there are no"
-                          "registered handlers")
-
-    def test_emit_event_observing_handler(self):
-        EVENT_NAME = "event.hello.world"
-        callback_tracker = ['']
-
-        def my_callback(event_args, *args, **kwargs):
-            self.assertDictEqual(event_args,
-                                 {'sender': self,
-                                  'event': EVENT_NAME})
-            self.assertSequenceEqual(args, ['first_pos_arg'])
-            self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
-            callback_tracker[0] += 'obs'
-            return "hello"
-
-        dispatcher = SimpleEventDispatcher()
-        handler = dispatcher.observe(event_pattern=EVENT_NAME, priority=1000,
-                                     callback=my_callback)
-        assert handler.event_pattern == EVENT_NAME
-        assert handler.priority == 1000
-        assert handler.callback == my_callback
-        self.assertIsInstance(handler, EventHandler)
-        result = dispatcher.dispatch(self, EVENT_NAME, 'first_pos_arg',
-                                     a_keyword_arg='another_thing')
-        self.assertEqual(
-            callback_tracker[0], "obs", "callback should have been invoked"
-            "once and contain value `obs` but tracker value is {0}".format(
-                callback_tracker[0]))
-        self.assertIsNone(result, "Result should be none as this is an"
-                          " observing handler")
-
-    def test_emit_event_intercepting_handler(self):
-        EVENT_NAME = "event.hello.world"
-        callback_tracker = ['']
-
-        def my_callback(event_args, *args, **kwargs):
-            self.assertDictEqual(event_args,
-                                 {'sender': self,
-                                  'event': EVENT_NAME,
-                                  'next_handler': None})
-            self.assertSequenceEqual(args, ['first_pos_arg'])
-            self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
-            callback_tracker[0] += "intcpt"
-            return "world"
-
-        dispatcher = SimpleEventDispatcher()
-        handler = dispatcher.intercept(event_pattern=EVENT_NAME, priority=1000,
-                                       callback=my_callback)
-        self.assertIsInstance(handler, EventHandler)
-        result = dispatcher.dispatch(self, EVENT_NAME, 'first_pos_arg',
-                                     a_keyword_arg='another_thing')
-        self.assertEqual(
-            callback_tracker[0], "intcpt", "callback should have been invoked"
-            "once and contain value `intcpt` but tracker value is {0}".format(
-                callback_tracker[0]))
-        self.assertEqual(result, "world", "Result should be `world` as this"
-                         " is an intercepting handler")
-
-    def test_emit_event_implementing_handler(self):
-        EVENT_NAME = "event.hello.world"
-        callback_tracker = ['']
-
-        def my_callback(*args, **kwargs):
-            self.assertSequenceEqual(args, ['first_pos_arg'])
-            self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
-            callback_tracker[0] += "impl"
-            return "world"
-
-        dispatcher = SimpleEventDispatcher()
-        handler = dispatcher.implement(event_pattern=EVENT_NAME, priority=1000,
-                                       callback=my_callback)
-        self.assertIsInstance(handler, EventHandler)
-        result = dispatcher.dispatch(self, EVENT_NAME, 'first_pos_arg',
-                                     a_keyword_arg='another_thing')
-        self.assertEqual(
-            callback_tracker[0], "impl", "callback should have been invoked"
-            "once and contain value `intcpt` but tracker value is {0}".format(
-                callback_tracker[0]))
-        self.assertEqual(result, "world", "Result should be `world` as this"
-                         " is an implementing handler")
-
-    def test_emit_event_observe_precedes_intercept(self):
-        EVENT_NAME = "event.hello.world"
-        callback_tracker = ['']
-
-        def my_callback_obs(event_args, *args, **kwargs):
-            self.assertDictEqual(event_args,
-                                 {'sender': self,
-                                  'event': EVENT_NAME})
-            self.assertSequenceEqual(args, ['first_pos_arg'])
-            self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
-            callback_tracker[0] += "obs_"
-            return "hello"
-
-        def my_callback_intcpt(event_args, *args, **kwargs):
-            self.assertDictEqual(event_args,
-                                 {'sender': self,
-                                  'event': EVENT_NAME,
-                                  'next_handler': None})
-            self.assertSequenceEqual(args, ['first_pos_arg'])
-            self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
-            callback_tracker[0] += "intcpt_"
-            return "world"
-
-        dispatcher = SimpleEventDispatcher()
-        dispatcher.observe(EVENT_NAME, 1000, my_callback_obs)
-        dispatcher.intercept(EVENT_NAME, 1001, my_callback_intcpt)
-        result = dispatcher.dispatch(self, EVENT_NAME, 'first_pos_arg',
-                                     a_keyword_arg='another_thing')
-        self.assertEqual(
-            callback_tracker[0], "obs_intcpt_", "callback was not invoked in "
-            "expected order. Should have been obs_intcpt_ but is {0}".format(
-                callback_tracker[0]))
-        self.assertEqual(result, "world", "Result should be `world` as this"
-                         " is the return value of the intercepting handler")
-
-    def test_emit_event_observe_follows_intercept(self):
-        EVENT_NAME = "event.hello.world"
-        callback_tracker = ['']
-
-        def my_callback_intcpt(event_args, *args, **kwargs):
-            self.assertSequenceEqual(args, ['first_pos_arg'])
-            self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
-            self.assertEqual(event_args.get('sender'), self)
-            self.assertEqual(event_args.get('next_handler').priority, 1001)
-            self.assertEqual(event_args.get('next_handler').callback.__name__,
-                             "my_callback_obs")
-            callback_tracker[0] += "intcpt_"
-            # invoke next handler
-            next_handler = event_args.get('next_handler')
-            assert next_handler.priority == 1001
-            assert next_handler.event_pattern == EVENT_NAME
-            assert next_handler.callback == my_callback_obs
-            retval = next_handler.invoke(event_args, *args, **kwargs)
-            self.assertIsNone(retval, "Return values of observable handlers"
-                              " should not be propagated.")
-            return "world"
-
-        def my_callback_obs(event_args, *args, **kwargs):
-            self.assertSequenceEqual(args, ['first_pos_arg'])
-            self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
-            self.assertDictEqual(event_args,
-                                 {'sender': self,
-                                  'event': EVENT_NAME})
-            callback_tracker[0] += "obs_"
-            return "hello"
-
-        dispatcher = SimpleEventDispatcher()
-        # register priorities out of order to test that too
-        dispatcher.observe(EVENT_NAME, 1001, my_callback_obs)
-        dispatcher.intercept(EVENT_NAME, 1000, my_callback_intcpt)
-        result = dispatcher.dispatch(self, EVENT_NAME, 'first_pos_arg',
-                                     a_keyword_arg='another_thing')
-        self.assertEqual(
-            callback_tracker[0], "intcpt_obs_", "callback was not invoked in "
-            "expected order. Should have been intcpt_obs_ but is {0}".format(
-                callback_tracker[0]))
-        self.assertEqual(result, "world", "Result should be `world` as this"
-                         " is the return value of the intercepting handler")
-
-    def test_emit_event_intercept_follows_intercept(self):
-        EVENT_NAME = "event.hello.world"
-        callback_tracker = ['']
-
-        def my_callback_intcpt1(event_args, *args, **kwargs):
-            self.assertEqual(event_args.get('sender'), self)
-            next_handler = event_args.get('next_handler')
-            self.assertEqual(next_handler.priority, 2020)
-            self.assertEqual(next_handler.callback.__name__,
-                             "my_callback_intcpt2")
-            callback_tracker[0] += "intcpt1_"
-            # invoke next handler but ignore return value
-            return "hello" + next_handler.invoke(event_args, *args, **kwargs)
-
-        def my_callback_intcpt2(event_args, *args, **kwargs):
-            self.assertDictEqual(event_args,
-                                 {'sender': self,
-                                  'event': EVENT_NAME,
-                                  'next_handler': None})
-            callback_tracker[0] += "intcpt2_"
-            return "world"
-
-        dispatcher = SimpleEventDispatcher()
-        dispatcher.intercept(EVENT_NAME, 2000, my_callback_intcpt1)
-        dispatcher.intercept(EVENT_NAME, 2020, my_callback_intcpt2)
-        result = dispatcher.dispatch(self, EVENT_NAME)
-        self.assertEqual(
-            callback_tracker[0], "intcpt1_intcpt2_", "callback was not invoked"
-            " in expected order. Should have been intcpt1_intcpt2_ but is"
-            " {0}".format(callback_tracker[0]))
-        self.assertEqual(result, "helloworld", "Result should be `helloworld` "
-                         "as this is the expected return value from the chain")
-
-    def test_emit_event_implement_follows_intercept(self):
-        EVENT_NAME = "event.hello.world"
-        callback_tracker = ['']
-
-        def my_callback_intcpt(event_args, *args, **kwargs):
-            self.assertEqual(event_args.get('sender'), self)
-            next_handler = event_args.get('next_handler')
-            self.assertEqual(next_handler.priority, 2020)
-            self.assertEqual(next_handler.callback.__name__,
-                             "my_callback_impl")
-            callback_tracker[0] += "intcpt_"
-            # invoke next handler but ignore return value
-            return "hello" + next_handler.invoke(event_args, *args, **kwargs)
-
-        def my_callback_impl(*args, **kwargs):
-            self.assertSequenceEqual(args, ['first_pos_arg'])
-            self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
-            callback_tracker[0] += "impl_"
-            return "world"
-
-        dispatcher = SimpleEventDispatcher()
-        dispatcher.intercept(EVENT_NAME, 2000, my_callback_intcpt)
-        dispatcher.implement(EVENT_NAME, 2020, my_callback_impl)
-        result = dispatcher.dispatch(self, EVENT_NAME, 'first_pos_arg',
-                                     a_keyword_arg='another_thing')
-        self.assertEqual(
-            callback_tracker[0], "intcpt_impl_", "callback was not invoked"
-            " in expected order. Should have been intcpt_impl_ but is"
-            " {0}".format(callback_tracker[0]))
-        self.assertEqual(result, "helloworld", "Result should be `helloworld` "
-                         "as this is the expected return value from the chain")
-
-    def test_emit_event_implement_precedes_intercept(self):
-        EVENT_NAME = "event.hello.world"
-        callback_tracker = ['']
-
-        def my_callback_intcpt(event_args, *args, **kwargs):
-            # Impl result should be accessible to intercepts that follow
-            self.assertDictEqual(event_args,
-                                 {'sender': self,
-                                  'event': EVENT_NAME,
-                                  'result': 'world',
-                                  'next_handler': None})
-            self.assertSequenceEqual(args, ['first_pos_arg'])
-            self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
-            callback_tracker[0] += "intcpt_"
-            return "hello"
-
-        def my_callback_impl(*args, **kwargs):
-            self.assertSequenceEqual(args, ['first_pos_arg'])
-            self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
-            callback_tracker[0] += "impl_"
-            return "world"
-
-        dispatcher = SimpleEventDispatcher()
-        dispatcher.implement(EVENT_NAME, 2000, my_callback_impl)
-        dispatcher.intercept(EVENT_NAME, 2020, my_callback_intcpt)
-        result = dispatcher.dispatch(self, EVENT_NAME, 'first_pos_arg',
-                                     a_keyword_arg='another_thing')
-        self.assertEqual(
-            callback_tracker[0], "impl_intcpt_", "callback was not invoked"
-            " in expected order. Should have been intcpt_intcpt_ but is"
-            " {0}".format(callback_tracker[0]))
-        self.assertEqual(result, "world", "Result should be `world` "
-                         "as this is the expected return value from the chain")
-
-    def test_subscribe_event_duplicate_priority(self):
-
-        def my_callback(event_args, *args, **kwargs):
-            pass
-
-        dispatcher = SimpleEventDispatcher()
-        dispatcher.intercept("event.hello.world", 1000, my_callback)
-        dispatcher.intercept("event.hello.world", 1000, my_callback)
-        with self.assertRaises(HandlerException):
-            dispatcher.dispatch(self, "event.hello.world")
-
-    def test_subscribe_event_duplicate_wildcard_priority(self):
-
-        def my_callback(event_args, *args, **kwargs):
-            pass
-
-        dispatcher = SimpleEventDispatcher()
-        dispatcher.intercept("event.hello.world", 1000, my_callback)
-        dispatcher.intercept("event.hello.*", 1000, my_callback)
-        with self.assertRaises(HandlerException):
-            dispatcher.dispatch(self, "event.hello.world")
-
-    def test_subscribe_event_duplicate_wildcard_priority_allowed(self):
-        # duplicate priorities for different wildcard namespaces allowed
-        def my_callback(event_args, *args, **kwargs):
-            pass
-
-        dispatcher = SimpleEventDispatcher()
-        dispatcher.intercept("event.hello.world", 1000, my_callback)
-        dispatcher.intercept("someevent.hello.*", 1000, my_callback)
-        # emit should work fine in this case with no exceptions
-        dispatcher.dispatch(self, "event.hello.world")
-
-    def test_subscribe_multiple_events(self):
-        EVENT_NAME = "event.hello.world"
-        callback_tracker = ['']
-
-        def my_callback1(event_args, *args, **kwargs):
-            self.assertDictEqual(event_args, {'sender': self,
-                                              'event': EVENT_NAME})
-            callback_tracker[0] += "event1_"
-            return "hello"
-
-        def my_callback2(event_args, *args, **kwargs):
-            self.assertDictEqual(event_args,
-                                 {'sender': self,
-                                  'event': "event.hello.anotherworld"})
-            callback_tracker[0] += "event2_"
-            return "another"
-
-        def my_callback3(event_args, *args, **kwargs):
-            self.assertDictEqual(event_args,
-                                 {'sender': self,
-                                  'event': "event.hello.anotherworld",
-                                  'next_handler': None})
-            callback_tracker[0] += "event3_"
-            return "world"
-
-        dispatcher = SimpleEventDispatcher()
-        dispatcher.observe(EVENT_NAME, 2000, my_callback1)
-        # register to a different event with the same priority
-        dispatcher.observe("event.hello.anotherworld", 2000, my_callback2)
-        dispatcher.intercept("event.hello.anotherworld", 2020, my_callback3)
-        result = dispatcher.dispatch(self, EVENT_NAME)
-        self.assertEqual(
-            callback_tracker[0], "event1_", "only `event.hello.world` handlers"
-            " should have been  triggered but received {0}".format(
-                callback_tracker[0]))
-        self.assertEqual(result, None, "Result should be `helloworld` "
-                         "as this is the expected return value from the chain")
-
-        result = dispatcher.dispatch(self, "event.hello.anotherworld")
-        self.assertEqual(
-            callback_tracker[0], "event1_event2_event3_", "only handlers for"
-            "  event `event.hello.anotherworld` should have been  triggered"
-            " but received {0}".format(callback_tracker[0]))
-        self.assertEqual(result, "world", "Result should be `world` "
-                         "as this is the expected return value from the chain")
-
-    def test_subscribe_wildcard(self):
-        callback_tracker = ['']
-
-        def my_callback1(event_args, *args, **kwargs):
-            callback_tracker[0] += "event1_"
-            next_handler = event_args.get('next_handler')
-            return "hello" + next_handler.invoke(event_args, *args, **kwargs)
-
-        def my_callback2(event_args, *args, **kwargs):
-            callback_tracker[0] += "event2_"
-            next_handler = event_args.get('next_handler')
-            return "some" + next_handler.invoke(event_args, *args, **kwargs)
-
-        def my_callback3(event_args, *args, **kwargs):
-            callback_tracker[0] += "event3_"
-            next_handler = event_args.get('next_handler')
-            return "other" + next_handler.invoke(event_args, *args, **kwargs)
-
-        def my_callback4(*args, **kwargs):
-            callback_tracker[0] += "event4_"
-            return "world"
-
-        dispatcher = SimpleEventDispatcher()
-        dispatcher.intercept("event.*", 2000, my_callback1)
-        # register to a different event with the same priority
-        dispatcher.intercept("event.hello.*", 2010, my_callback2)
-        dispatcher.intercept("event.hello.there", 2030, my_callback4)
-        dispatcher.intercept("event.*.there", 2020, my_callback3)
-        dispatcher.intercept("event.*.world", 2020, my_callback4)
-        dispatcher.intercept("someevent.hello.there", 2030, my_callback3)
-        # emit a series of events
-        result = dispatcher.dispatch(self, "event.hello.there")
-
-        self.assertEqual(
-            callback_tracker[0], "event1_event2_event3_event4_",
-            "Event handlers executed in unexpected order {0}".format(
-                callback_tracker[0]))
-        self.assertEqual(result, "hellosomeotherworld")
-
-        result = dispatcher.dispatch(self, "event.test.hello.world")
-        self.assertEqual(
-            callback_tracker[0], "event1_event2_event3_event4_event1_event4_",
-            "Event handlers executed in unexpected order {0}".format(
-                callback_tracker[0]))
-        self.assertEqual(result, "helloworld")
-
-    # make sure cache gets invalidated when subscribing after emit
-    def test_subscribe_after_emit(self):
-        callback_tracker = ['']
-
-        def my_callback1(event_args, *args, **kwargs):
-            callback_tracker[0] += "event1_"
-            next_handler = event_args.get('next_handler')
-            if next_handler:
-                return "hello" + next_handler.invoke(
-                    event_args, *args, **kwargs)
-            else:
-                return "hello"
-
-        def my_callback2(*args, **kwargs):
-            callback_tracker[0] += "event2_"
-            return "some"
-
-        dispatcher = SimpleEventDispatcher()
-        dispatcher.intercept("event.hello.world", 1000, my_callback1)
-        dispatcher.dispatch(self, "event.hello.world")
-        dispatcher.intercept("event.hello.*", 1001, my_callback2)
-        result = dispatcher.dispatch(self, "event.hello.world")
-
-        self.assertEqual(
-            callback_tracker[0], "event1_event1_event2_",
-            "Event handlers executed in unexpected order {0}".format(
-                callback_tracker[0]))
-        self.assertEqual(result, "hellosome")
-
-    def test_unubscribe(self):
-        EVENT_NAME = "event.hello.world"
-        callback_tracker = ['']
-
-        def my_callback1(event_args, *args, **kwargs):
-            callback_tracker[0] += "event1_"
-            next_handler = event_args.get('next_handler')
-            if next_handler:
-                return "hello" + next_handler.invoke(
-                    event_args, *args, **kwargs)
-            else:
-                return "hello"
-
-        def my_callback2(*args, **kwargs):
-            callback_tracker[0] += "event2_"
-            return "some"
-
-        dispatcher = SimpleEventDispatcher()
-        hndlr1 = dispatcher.intercept(EVENT_NAME, 1000, my_callback1)
-        dispatcher.dispatch(self, EVENT_NAME)
-        hndlr2 = dispatcher.intercept("event.hello.*", 1001, my_callback2)
-        # Both handlers should be registered
-        self.assertListEqual(
-            [my_callback1, my_callback2],
-            [handler.callback for handler in
-             dispatcher.get_handlers_for_event(EVENT_NAME)])
-        hndlr1.unsubscribe()
-
-        # Only my_callback2 should be registered after unsubscribe
-        self.assertListEqual(
-            [my_callback2],
-            [handler.callback for handler in
-             dispatcher.get_handlers_for_event(EVENT_NAME)])
-
-        result = dispatcher.dispatch(self, EVENT_NAME)
-
-        self.assertEqual(
-            callback_tracker[0], "event1_event2_",
-            "Event handlers executed in unexpected order {0}".format(
-                callback_tracker[0]))
-        self.assertEqual(result, "some")
-
-        hndlr2.unsubscribe()
-        result = dispatcher.dispatch(self, "event.hello.world")
-        self.assertEqual(result, None)

+ 4 - 291
test/test_middleware_system.py

@@ -1,305 +1,18 @@
 import unittest
 import unittest
 
 
-from cloudbridge.cloud.base.events import SimpleEventDispatcher
-from cloudbridge.cloud.base.middleware import BaseMiddleware
+from pyeventsystem.events import SimpleEventDispatcher
+from pyeventsystem.middleware import SimpleMiddlewareManager
+from pyeventsystem.middleware import implement
+
 from cloudbridge.cloud.base.middleware import EventDebugLoggingMiddleware
 from cloudbridge.cloud.base.middleware import EventDebugLoggingMiddleware
 from cloudbridge.cloud.base.middleware import ExceptionWrappingMiddleware
 from cloudbridge.cloud.base.middleware import ExceptionWrappingMiddleware
-from cloudbridge.cloud.base.middleware import SimpleMiddlewareManager
-from cloudbridge.cloud.base.middleware import dispatch
-from cloudbridge.cloud.base.middleware import implement
-from cloudbridge.cloud.base.middleware import intercept
-from cloudbridge.cloud.base.middleware import observe
 from cloudbridge.cloud.interfaces.exceptions import CloudBridgeBaseException
 from cloudbridge.cloud.interfaces.exceptions import CloudBridgeBaseException
-from cloudbridge.cloud.interfaces.exceptions import HandlerException
 from cloudbridge.cloud.interfaces.exceptions import \
 from cloudbridge.cloud.interfaces.exceptions import \
     InvalidConfigurationException
     InvalidConfigurationException
-from cloudbridge.cloud.interfaces.middleware import Middleware
 
 
 from .helpers import skipIfPython
 from .helpers import skipIfPython
 
 
 
 
-class MiddlewareSystemTestCase(unittest.TestCase):
-
-    def test_basic_middleware(self):
-
-        class DummyMiddleWare(Middleware):
-
-            def __init__(self):
-                self.invocation_order = ""
-
-            def install(self, event_manager):
-                self.event_manager = event_manager
-                self.invocation_order += "install_"
-
-            def uninstall(self):
-                self.invocation_order += "uninstall"
-
-        dispatcher = SimpleEventDispatcher()
-        manager = SimpleMiddlewareManager(dispatcher)
-        middleware = DummyMiddleWare()
-        manager.add(middleware)
-
-        self.assertEqual(middleware.invocation_order, "install_",
-                         "install should be called when adding new middleware")
-
-        manager.remove(middleware)
-        self.assertEqual(middleware.invocation_order, "install_uninstall",
-                         "uninstall should be called when removing middleware")
-
-    def test_base_middleware(self):
-        EVENT_NAME = "some.event.occurred"
-
-        class DummyMiddleWare(BaseMiddleware):
-
-            def __init__(self):
-                self.invocation_order = ""
-
-            @intercept(event_pattern="some.event.*", priority=900)
-            def my_callback_intcpt(self, event_args, *args, **kwargs):
-                self.invocation_order += "intcpt_"
-                assert 'first_pos_arg' in args
-                assert kwargs.get('a_keyword_arg') == "something"
-                next_handler = event_args.get('next_handler')
-                return next_handler.invoke(event_args, *args, **kwargs)
-
-            @implement(event_pattern="some.event.*", priority=950)
-            def my_callback_impl(self, *args, **kwargs):
-                self.invocation_order += "impl_"
-                assert 'first_pos_arg' in args
-                assert kwargs.get('a_keyword_arg') == "something"
-                return "hello"
-
-            @observe(event_pattern="some.event.*", priority=1000)
-            def my_callback_obs(self, event_args, *args, **kwargs):
-                self.invocation_order += "obs"
-                assert 'first_pos_arg' in args
-                assert event_args['result'] == "hello"
-                assert kwargs.get('a_keyword_arg') == "something"
-
-        dispatcher = SimpleEventDispatcher()
-        manager = SimpleMiddlewareManager(dispatcher)
-        middleware = DummyMiddleWare()
-        manager.add(middleware)
-        dispatcher.dispatch(self, EVENT_NAME, 'first_pos_arg',
-                            a_keyword_arg='something')
-
-        self.assertEqual(middleware.invocation_order, "intcpt_impl_obs")
-        self.assertListEqual(
-            [middleware.my_callback_intcpt, middleware.my_callback_impl,
-             middleware.my_callback_obs],
-            [handler.callback for handler
-             in dispatcher.get_handlers_for_event(EVENT_NAME)])
-
-        manager.remove(middleware)
-
-        self.assertListEqual([], dispatcher.get_handlers_for_event(EVENT_NAME))
-
-    def test_multiple_middleware(self):
-        EVENT_NAME = "some.really.interesting.event.occurred"
-
-        class DummyMiddleWare1(BaseMiddleware):
-
-            @observe(event_pattern="some.really.*", priority=1000)
-            def my_obs1_3(self, *args, **kwargs):
-                pass
-
-            @implement(event_pattern="some.*", priority=970)
-            def my_impl1_2(self, *args, **kwargs):
-                return "hello"
-
-            @intercept(event_pattern="some.*", priority=900)
-            def my_intcpt1_1(self, event_args, *args, **kwargs):
-                next_handler = event_args.get('next_handler')
-                return next_handler.invoke(event_args, *args, **kwargs)
-
-        class DummyMiddleWare2(BaseMiddleware):
-
-            @observe(event_pattern="some.really.*", priority=1050)
-            def my_obs2_3(self, *args, **kwargs):
-                pass
-
-            @intercept(event_pattern="*", priority=950)
-            def my_intcpt2_2(self, event_args, *args, **kwargs):
-                next_handler = event_args.get('next_handler')
-                return next_handler.invoke(event_args, *args, **kwargs)
-
-            @implement(event_pattern="some.really.*", priority=920)
-            def my_impl2_1(self, *args, **kwargs):
-                pass
-
-        dispatcher = SimpleEventDispatcher()
-        manager = SimpleMiddlewareManager(dispatcher)
-        middleware1 = DummyMiddleWare1()
-        middleware2 = DummyMiddleWare2()
-        manager.add(middleware1)
-        manager.add(middleware2)
-        dispatcher.dispatch(self, EVENT_NAME)
-
-        # Callbacks in both middleware classes should be registered
-        self.assertListEqual(
-            [middleware1.my_intcpt1_1, middleware2.my_impl2_1,
-             middleware2.my_intcpt2_2, middleware1.my_impl1_2,
-             middleware1.my_obs1_3, middleware2.my_obs2_3],
-            [handler.callback for handler
-             in dispatcher.get_handlers_for_event(EVENT_NAME)])
-
-        manager.remove(middleware1)
-
-        # Only middleware2 callbacks should be registered
-        self.assertListEqual(
-            [middleware2.my_impl2_1, middleware2.my_intcpt2_2,
-             middleware2.my_obs2_3],
-            [handler.callback for handler in
-             dispatcher.get_handlers_for_event(EVENT_NAME)])
-
-        # add middleware back to check that internal state is properly handled
-        manager.add(middleware1)
-
-        # should one again equal original list
-        self.assertListEqual(
-            [middleware1.my_intcpt1_1, middleware2.my_impl2_1,
-             middleware2.my_intcpt2_2, middleware1.my_impl1_2,
-             middleware1.my_obs1_3, middleware2.my_obs2_3],
-            [handler.callback for handler
-             in dispatcher.get_handlers_for_event(EVENT_NAME)])
-
-    def test_automatic_middleware(self):
-        EVENT_NAME = "another.interesting.event.occurred"
-
-        class SomeDummyClass(object):
-
-            @observe(event_pattern="another.really.*", priority=1000)
-            def not_a_match(self, *args, **kwargs):
-                pass
-
-            @intercept(event_pattern="another.*", priority=900)
-            def my_callback_intcpt2(self, *args, **kwargs):
-                pass
-
-            def not_an_event_handler(self, *args, **kwargs):
-                pass
-
-            @observe(event_pattern="another.interesting.*", priority=1000)
-            def my_callback_obs1(self, *args, **kwargs):
-                pass
-
-            @implement(event_pattern="another.interesting.*", priority=1050)
-            def my_callback_impl(self, *args, **kwargs):
-                pass
-
-        dispatcher = SimpleEventDispatcher()
-        manager = SimpleMiddlewareManager(dispatcher)
-        some_obj = SomeDummyClass()
-        middleware = manager.add(some_obj)
-        dispatcher.dispatch(self, EVENT_NAME)
-
-        # Middleware should be discovered even if class containing interceptors
-        # doesn't inherit from Middleware
-        self.assertListEqual(
-            [some_obj.my_callback_intcpt2, some_obj.my_callback_obs1,
-             some_obj.my_callback_impl],
-            [handler.callback for handler
-             in dispatcher.get_handlers_for_event(EVENT_NAME)])
-
-        manager.remove(middleware)
-
-        # Callbacks should be correctly removed
-        self.assertListEqual(
-            [],
-            [handler.callback for handler in
-             dispatcher.get_handlers_for_event(EVENT_NAME)])
-
-    def test_event_decorator(self):
-        EVENT_NAME = "some.event.occurred"
-
-        class SomeDummyClass(object):
-
-            def __init__(self):
-                self.invocation_order = ""
-                self.events = SimpleEventDispatcher()
-
-            @intercept(event_pattern="some.event.*", priority=900)
-            def my_callback_intcpt(self, event_args, *args, **kwargs):
-                self.invocation_order += "intcpt_"
-                assert 'first_pos_arg' in args
-                assert kwargs.get('a_keyword_arg') == "something"
-                next_handler = event_args.get('next_handler')
-                return next_handler.invoke(event_args, *args, **kwargs)
-
-            @observe(event_pattern="some.event.*", priority=3000)
-            def my_callback_obs(self, event_args, *args, **kwargs):
-                self.invocation_order += "obs"
-                assert 'first_pos_arg' in args
-                assert event_args['result'] == "hello"
-                assert kwargs.get('a_keyword_arg') == "something"
-
-            @dispatch(event=EVENT_NAME, priority=2500)
-            def my_callback_impl(self, *args, **kwargs):
-                self.invocation_order += "impl_"
-                assert 'first_pos_arg' in args
-                assert kwargs.get('a_keyword_arg') == "something"
-                return "hello"
-
-        obj = SomeDummyClass()
-        manager = SimpleMiddlewareManager(obj.events)
-        middleware = manager.add(obj)
-
-        # calling my_implementation should trigger all events
-        result = obj.my_callback_impl(
-            'first_pos_arg', a_keyword_arg='something')
-
-        self.assertEqual(result, "hello")
-        self.assertEqual(obj.invocation_order, "intcpt_impl_obs")
-        callbacks = [handler.callback for handler
-                     in middleware.events.get_handlers_for_event(EVENT_NAME)]
-
-        self.assertNotIn(
-            obj.my_callback_impl, callbacks,
-            "The event impl callback should not be directly contained"
-            " in callbacks to avoid a circular dispatch")
-
-        self.assertEqual(
-            len(set(callbacks).difference(
-                set([obj.my_callback_intcpt,
-                     obj.my_callback_obs]))),
-            1,
-            "The event impl callback should be included in the list of"
-            "  callbacks indirectly")
-
-        manager.remove(middleware)
-
-        # calling my_implementation again should trigger a None response
-        result = obj.my_callback_impl(
-            'first_pos_arg', a_keyword_arg='something')
-
-        self.assertEqual(result, None)
-
-    def test_event_decorator_no_event_property(self):
-        EVENT_NAME = "some.event.occurred"
-
-        class SomeDummyClass(object):
-
-            @dispatch(event=EVENT_NAME, priority=2500)
-            def my_callback_impl(self, *args, **kwargs):
-                assert 'first_pos_arg' in args
-                assert kwargs.get('a_keyword_arg') == "something"
-                return "hello"
-
-        obj = SomeDummyClass()
-        events = SimpleEventDispatcher()
-        manager = SimpleMiddlewareManager(events)
-        manager.add(obj)
-
-        # calling my_implementation should raise an exception
-        with self.assertRaises(HandlerException):
-            obj.my_callback_impl('first_pos_arg', a_keyword_arg='something')
-
-        obj.events = events
-        result = obj.my_callback_impl('first_pos_arg',
-                                      a_keyword_arg='something')
-        self.assertEqual(result, "hello")
-
-
 class ExceptionWrappingMiddlewareTestCase(unittest.TestCase):
 class ExceptionWrappingMiddlewareTestCase(unittest.TestCase):
 
 
     def test_unknown_exception_is_wrapped(self):
     def test_unknown_exception_is_wrapped(self):