-
Notifications
You must be signed in to change notification settings - Fork 51
[WIP] Adding middleware support in factory #195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
1c10169
e827905
0ecadbe
614ca04
901b4b6
21f817a
25390d4
eea4356
aa50481
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,11 @@ | |
| import pkgutil | ||
| from collections import defaultdict | ||
|
|
||
| from pyeventsystem.events import SimpleEventDispatcher | ||
| from pyeventsystem.interfaces import Middleware | ||
| from pyeventsystem.middleware import AutoDiscoveredMiddleware | ||
| from pyeventsystem.middleware import SimpleMiddlewareManager | ||
|
|
||
| from cloudbridge.cloud import providers | ||
| from cloudbridge.cloud.interfaces import CloudProvider | ||
| from cloudbridge.cloud.interfaces import TestMockHelperMixin | ||
|
|
@@ -12,6 +17,192 @@ | |
| log = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| # Todo: Move to pyeventsystem if we're keeping this logic | ||
| class ParentMiddlewareGenerator(SimpleMiddlewareManager): | ||
|
|
||
| def __init__(self, event_manager=None): | ||
| super(ParentMiddlewareGenerator, self).__init__(event_manager) | ||
| self.middleware_constructors = [] | ||
|
|
||
| def add_middleware_class(self, middleware_class, *args, **kwargs): | ||
| self.middleware_constructors.append((middleware_class, args, kwargs)) | ||
|
|
||
| def remove_middleware_class(self, middleware_class, *args, **kwargs): | ||
| self.middleware_constructors.remove((middleware_class, args, kwargs)) | ||
|
|
||
| def get_directly_subscribed_handlers(self): | ||
| all_handlers = [] | ||
| # Todo: Expose this better in pyeventsystem library | ||
| event_dict = self.events._SimpleEventDispatcher__events | ||
| for key in event_dict.keys(): | ||
| for h in event_dict[key]: | ||
| all_handlers.append((h, | ||
| h.event_pattern, | ||
| h.priority, | ||
| h.callback)) | ||
| middleware_handlers = [] | ||
| for m in self.middleware_list: | ||
| obj = (m.obj_to_discover if isinstance(m, AutoDiscoveredMiddleware) | ||
| else m) | ||
| handlers = m.discover_handlers(obj) | ||
| for h in handlers: | ||
| middleware_handlers.append((h.event_pattern, h.priority, | ||
| h.callback)) | ||
| direct_subs = [h for h, e, p, c in all_handlers | ||
| if (e, p, c) not in middleware_handlers] | ||
| return direct_subs | ||
|
|
||
| def generate_child_manager(self, namespace=None): | ||
| event_dispatcher = None | ||
| if namespace: | ||
| event_dispatcher = NamespacedEventDispatcher(namespace) | ||
| manager = ChildMiddlewareManager(self, event_dispatcher) | ||
| manager.inherit_handlers() | ||
| return manager | ||
|
|
||
|
|
||
| # TODO: Move to pyeventsystem if we're keeping | ||
| class NamespacedEventDispatcher(SimpleEventDispatcher): | ||
|
|
||
| def __init__(self, namespace): | ||
| super(NamespacedEventDispatcher, self).__init__() | ||
| self._namespace = namespace | ||
|
|
||
| @property | ||
| def namespace(self): | ||
| return self._namespace | ||
|
|
||
| def get_handlers_for_event(self, event): | ||
| event = ".".join((self._namespace, event)) | ||
| return self.get_handlers_for_event_direct(event) | ||
|
|
||
| def _create_handler_cache(self, event): | ||
| event = ".".join((self._namespace, event)) | ||
| return self._create_handler_cache_direct(event) | ||
|
|
||
| def _invalidate_cache(self, event_pattern): | ||
| event_pattern = ".".join((self._namespace, event_pattern)) | ||
| return self._invalidate_cache_direct(event_pattern) | ||
|
|
||
| def dispatch(self, sender, event, *args, **kwargs): | ||
| event = ".".join((self._namespace, event)) | ||
| return self.dispatch_direct(sender, event, *args, **kwargs) | ||
|
|
||
| def observe(self, event_pattern, priority, callback): | ||
| event_pattern = ".".join((self._namespace, event_pattern)) | ||
| return self.observe_direct(event_pattern, priority, callback) | ||
|
|
||
| def intercept(self, event_pattern, priority, callback): | ||
| event_pattern = ".".join((self._namespace, event_pattern)) | ||
| return self.intercept_direct(event_pattern, priority, callback) | ||
|
|
||
| def implement(self, event_pattern, priority, callback): | ||
| event_pattern = ".".join((self._namespace, event_pattern)) | ||
| return self.implement_direct(event_pattern, priority, callback) | ||
|
|
||
| def observe_direct(self, event_pattern, priority, callback): | ||
| return super(NamespacedEventDispatcher, self).observe(event_pattern, | ||
| priority, | ||
| callback) | ||
|
|
||
| def intercept_direct(self, event_pattern, priority, callback): | ||
| return super(NamespacedEventDispatcher, self).intercept(event_pattern, | ||
| priority, | ||
| callback) | ||
|
|
||
| def implement_direct(self, event_pattern, priority, callback): | ||
| return super(NamespacedEventDispatcher, self).implement(event_pattern, | ||
| priority, | ||
| callback) | ||
|
|
||
| def dispatch_direct(self, sender, event, *args, **kwargs): | ||
| return super(NamespacedEventDispatcher, self).dispatch(sender, event, | ||
| *args, **kwargs) | ||
|
|
||
| def get_handlers_for_event_direct(self, event): | ||
| return super(NamespacedEventDispatcher, self).get_handlers_for_event( | ||
| event) | ||
|
|
||
| def _create_handler_cache_direct(self, event): | ||
| return super(NamespacedEventDispatcher, self)._create_handler_cache( | ||
| event) | ||
|
|
||
| def _invalidate_cache_direct(self, event_pattern): | ||
| return super(NamespacedEventDispatcher, self)._invalidate_cache( | ||
| event_pattern) | ||
|
|
||
|
|
||
| # TODO: Move to pyeventsystem if we're keeping | ||
| class ChildMiddlewareManager(SimpleMiddlewareManager): | ||
|
|
||
| def __init__(self, parent_manager, event_manager=None): | ||
| super(ChildMiddlewareManager, self).__init__(event_manager) | ||
| self._parent_manager = parent_manager | ||
| self._inherited = {} | ||
|
|
||
| def inherit_handlers(self): | ||
| self.remove_inherited_handlers() | ||
| middleware_list = [] | ||
| for middleware in self._parent_manager.middleware_list: | ||
| added = self.add_direct(middleware) | ||
| middleware_list.append(added) | ||
| for constructor, args, kwargs in (self._parent_manager | ||
| .middleware_constructors): | ||
| m = constructor(*args, **kwargs) | ||
| added = self.add_direct(m) | ||
| middleware_list.append(added) | ||
| self._inherited['middleware_list'] = middleware_list | ||
| handler_list = [] | ||
| for handler in self._parent_manager.get_directly_subscribed_handlers(): | ||
| new_handler = handler.__class__(handler.event_pattern, | ||
| handler.priority, | ||
| handler.callback) | ||
| handler_list.append(new_handler) | ||
| self.events.subscribe(new_handler) | ||
| self._inherited['handler_list'] = handler_list | ||
|
|
||
| def remove_inherited_handlers(self): | ||
| for m in self._inherited.get("middleware_list", []): | ||
| self.remove(m) | ||
|
|
||
| for h in self._inherited.get("handler_list", []): | ||
| self.events.unsubscribe(h) | ||
|
|
||
| self._inherited = {} | ||
|
|
||
| @property | ||
| def parent_manager(self): | ||
| return self._parent_manager | ||
|
|
||
| def add_direct(self, middleware): | ||
| return super(ChildMiddlewareManager, self).add(middleware) | ||
|
|
||
| def add(self, middleware): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we should provide dual semantics here - one for shared middleware objects, and another for independent middleware, just to keep it simple. I'm currently leaning in favour of the factory namespacing child event dispatchers, and forwarding them as necessary, so that we still maintain event isolation within a given provider. I think we probably just need to avoid a scenario where providers are unwittingly subscribing to events outside of the provider. |
||
| if isinstance(middleware, Middleware): | ||
| m = middleware | ||
| m.events = self.events | ||
| discovered_handlers = m.discover_handlers(m) | ||
| else: | ||
| m = AutoDiscoveredMiddleware(middleware) | ||
| m.events = self.events | ||
| discovered_handlers = m.discover_handlers(m.obj_to_discover) | ||
|
|
||
| # Rewrap handlers with namespaced event pattern if the event dispatcher | ||
| # is namespaced | ||
| if isinstance(self.events, NamespacedEventDispatcher): | ||
| for handler in discovered_handlers: | ||
| event_pattern = ".".join((self.events.namespace, | ||
| handler.event_pattern)) | ||
| new_handler = handler.__class__(event_pattern, | ||
| handler.priority, | ||
| handler.callback) | ||
| discovered_handlers.remove(handler) | ||
| discovered_handlers.append(new_handler) | ||
| m.add_handlers(discovered_handlers) | ||
| self.middleware_list.append(m) | ||
| return m | ||
|
|
||
|
|
||
| class ProviderList(object): | ||
| AWS = 'aws' | ||
| AZURE = 'azure' | ||
|
|
@@ -27,9 +218,14 @@ class CloudProviderFactory(object): | |
| """ | ||
|
|
||
| def __init__(self): | ||
| self._middleware = ParentMiddlewareGenerator() | ||
| self.provider_list = defaultdict(dict) | ||
| log.debug("Providers List: %s", self.provider_list) | ||
|
|
||
| @property | ||
| def middleware(self): | ||
| return self._middleware | ||
|
|
||
| def register_provider_class(self, cls): | ||
| """ | ||
| Registers a provider class with the factory. The class must | ||
|
|
@@ -136,7 +332,9 @@ def create_provider(self, name, config): | |
| 'A provider with name {0} could not be' | ||
| ' found'.format(name)) | ||
| log.debug("Created '%s' provider", name) | ||
| return provider_class(config) | ||
| namespaced_middleware = self.middleware.generate_child_manager(name) | ||
| return provider_class(config, | ||
| namespaced_middleware) | ||
|
|
||
| def get_provider_class(self, name): | ||
| """ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great, better than sending in middleware as a list.