Browse Source

Merge branch 'master' of https://github.com/CloudVE/cloudbridge

almahmoud 7 years ago
parent
commit
a13f3af6c0

+ 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 sys
 
+from pyeventsystem.middleware import dispatch as pyevent_dispatch
+from pyeventsystem.middleware import intercept
+from pyeventsystem.middleware import observe
+
 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 HandlerException
-from ..interfaces.middleware import Middleware
-from ..interfaces.middleware import MiddlewareManager
 
 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
     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")))
 
 
-class ExceptionWrappingMiddleware(BaseMiddleware):
+class ExceptionWrappingMiddleware(object):
     """
     Wraps all unhandled exceptions in cloudbridge exceptions.
     """

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

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

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

@@ -54,7 +54,7 @@ class BaseCloudService(CloudService):
 
     @property
     def events(self):
-        return self._provider.events
+        return self._provider.middleware.events
 
 
 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):
         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
                   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
     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]"

+ 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
 
-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 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 HandlerException
 from cloudbridge.cloud.interfaces.exceptions import \
     InvalidConfigurationException
-from cloudbridge.cloud.interfaces.middleware import Middleware
 
 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):
 
     def test_unknown_exception_is_wrapped(self):