Skip to content

Commit

Permalink
Move to a wrapping strategy for event aliasing
Browse files Browse the repository at this point in the history
This allows us to support custom event emitters much more seamlessly.
  • Loading branch information
JordonPhillips committed Sep 5, 2018
1 parent 384523c commit f8b98a0
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 27 deletions.
72 changes: 55 additions & 17 deletions botocore/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,48 +343,86 @@ def __copy__(self):
return new_instance


class AliasedEventEmitter(HierarchicalEmitter):
class EventAliaser(BaseEventHooks):
EVENT_ALIASES = {
'api.sagemaker': 'sagemaker'
}

def __init__(self, event_aliases=None):
super(AliasedEventEmitter, self).__init__()
def __init__(self, event_emitter, event_aliases=None):
self._event_aliases = event_aliases
if event_aliases is None:
self._event_aliases = self.EVENT_ALIASES
self._emitter = event_emitter

def _emit(self, event_name, kwargs, stop_on_response=False):
def emit(self, event_name, **kwargs):
aliased_event_name = self._alias_event_name(event_name)
return self._emitter.emit(aliased_event_name, **kwargs)

def emit_until_response(self, event_name, **kwargs):
aliased_event_name = self._alias_event_name(event_name)
return self._emitter.emit_until_response(aliased_event_name, **kwargs)

def register(self, event_name, handler, unique_id=None,
unique_id_uses_count=False):
aliased_event_name = self._alias_event_name(event_name)
return super(AliasedEventEmitter, self)._emit(
aliased_event_name, kwargs, stop_on_response
return self._emitter.register(
aliased_event_name, handler, unique_id, unique_id_uses_count
)

def _verify_and_register(self, event_name, handler, unique_id,
register_method, unique_id_uses_count):
def register_first(self, event_name, handler, unique_id=None,
unique_id_uses_count=False):
aliased_event_name = self._alias_event_name(event_name)
super(AliasedEventEmitter, self)._verify_and_register(
aliased_event_name, handler, unique_id, register_method,
unique_id_uses_count
return self._emitter.register_first(
aliased_event_name, handler, unique_id, unique_id_uses_count
)

def register_last(self, event_name, handler, unique_id=None,
unique_id_uses_count=False):
aliased_event_name = self._alias_event_name(event_name)
return self._emitter.register_last(
aliased_event_name, handler, unique_id, unique_id_uses_count
)

def unregister(self, event_name, handler=None, unique_id=None,
unique_id_uses_count=False):
aliased_event_name = self._alias_event_name(event_name)
super(AliasedEventEmitter, self).unregister(
return self._emitter.unregister(
aliased_event_name, handler, unique_id, unique_id_uses_count
)

def _alias_event_name(self, event_name):
for old_part, new_part in self._event_aliases.items():
if old_part in event_name:

# We can't simply do a string replace for everything, otherwise we
# might end up tranlating substrings that we never intended to
# translate. So for most cases we try to break the event up and
# replace by section.
if '.' not in old_part:
event_parts = event_name.split('.')
try:
# Theoretically a given event name could have the same part
# repeated, but in practice this doesn't happen
event_parts[event_parts.index(old_part)] = new_part
except ValueError:
continue
new_name = '.'.join(event_parts)
elif old_part in event_name:
new_name = event_name.replace(old_part, new_part)
logger.debug("Changing event name from %s to %s" % (
event_name, new_name
))
return new_name
else:
continue

logger.debug("Changing event name from %s to %s" % (
event_name, new_name
))
return new_name
return event_name

def __copy__(self):
return self.__class__(
copy.copy(self._emitter),
copy.copy(self._event_aliases)
)


class _PrefixTrie(object):
"""Specialized prefix trie that handles wildcards.
Expand Down
8 changes: 5 additions & 3 deletions botocore/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
from botocore.exceptions import UnknownServiceError, PartialCredentialsError
from botocore.errorfactory import ClientExceptionsFactory
from botocore import handlers
from botocore.hooks import AliasedEventEmitter, first_non_none_response
from botocore.hooks import HierarchicalEmitter, first_non_none_response
from botocore.hooks import EventAliaser
from botocore.loaders import create_loader
from botocore.parsers import ResponseParserFactory
from botocore.regions import EndpointResolver
Expand Down Expand Up @@ -139,9 +140,10 @@ def __init__(self, session_vars=None, event_hooks=None,
if session_vars:
self.session_var_map.update(session_vars)
if event_hooks is None:
self._events = AliasedEventEmitter()
self._original_handler = HierarchicalEmitter()
else:
self._events = event_hooks
self._original_handler = event_hooks
self._events = EventAliaser(self._original_handler)
if include_builtin_handlers:
self._register_builtin_handlers(self._events)
self.user_agent_name = 'Botocore'
Expand Down
18 changes: 11 additions & 7 deletions tests/unit/test_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from functools import partial

from botocore.hooks import HierarchicalEmitter, first_non_none_response
from botocore.hooks import AliasedEventEmitter
from botocore.hooks import EventAliaser


class TestHierarchicalEventEmitter(unittest.TestCase):
Expand Down Expand Up @@ -62,40 +62,44 @@ def test_hook_called_in_proper_order(self):
self.assertEqual(calls, ['foo.bar.baz', 'foo.bar', 'foo'])


class TestAliasedEventEmitter(unittest.TestCase):
class TestAliasedEmitter(unittest.TestCase):
def setUp(self):
self.hook_calls = []

def hook(self, **kwargs):
self.hook_calls.append(kwargs)

def get_emitter(self, event_aliases):
emitter = HierarchicalEmitter()
return EventAliaser(emitter, event_aliases)

def test_event_emitted(self):
aliases = {'bar': 'bear'}
emitter = AliasedEventEmitter(event_aliases=aliases)
emitter = self.get_emitter(event_aliases=aliases)
emitter.register('foo.bear.baz', self.hook)
emitter.emit('foo.bear.baz')
calls = [e['event_name'] for e in self.hook_calls]
self.assertEqual(calls, ['foo.bear.baz'])

def test_aliased_event_emitted(self):
aliases = {'bar': 'bear'}
emitter = AliasedEventEmitter(event_aliases=aliases)
emitter = self.get_emitter(event_aliases=aliases)
emitter.register('foo.bear.baz', self.hook)
emitter.emit('foo.bar.baz')
calls = [e['event_name'] for e in self.hook_calls]
self.assertEqual(calls, ['foo.bear.baz'])

def test_aliased_event_registered(self):
aliases = {'bar': 'bear'}
emitter = AliasedEventEmitter(event_aliases=aliases)
emitter = self.get_emitter(event_aliases=aliases)
emitter.register('foo.bar.baz', self.hook)
emitter.emit('foo.bear.baz')
calls = [e['event_name'] for e in self.hook_calls]
self.assertEqual(calls, ['foo.bear.baz'])

def test_event_unregistered(self):
aliases = {'bar': 'bear'}
emitter = AliasedEventEmitter(event_aliases=aliases)
emitter = self.get_emitter(event_aliases=aliases)

emitter.register('foo.bar.baz', self.hook)
emitter.emit('foo.bear.baz')
Expand All @@ -110,7 +114,7 @@ def test_event_unregistered(self):

def test_aliased_event_unregistered(self):
aliases = {'bar': 'bear'}
emitter = AliasedEventEmitter(event_aliases=aliases)
emitter = self.get_emitter(event_aliases=aliases)

emitter.register('foo.bar.baz', self.hook)
emitter.emit('foo.bear.baz')
Expand Down

0 comments on commit f8b98a0

Please sign in to comment.