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