Add ExtensionManager.__getitem__

Allow extensions to be accessed directly by name.

Fixes issue #15

Signed-off-by: Doug Hellmann <doug.hellmann@dreamhost.com>
This commit is contained in:
Doug Hellmann 2013-06-03 10:54:49 -04:00
parent 52572efd4a
commit 69e17801a3
5 changed files with 79 additions and 0 deletions

View File

@ -12,6 +12,9 @@ dev
- Change the
:class:`~stevedore.dispatch.NamedDispatchExtensionManager` to ignore
missing extensions (:issue:`14`).
- Add ``__getitem__`` to
:class:`~stevedore.extension.ExtensionManager` for looking up
individual plugins by name (:issue:`15`).
0.8

View File

@ -55,6 +55,7 @@ class ExtensionManager(object):
self.extensions = self._load_plugins(invoke_on_load,
invoke_args,
invoke_kwds)
self._extensions_by_name = None
ENTRY_POINT_CACHE = {}
@ -93,6 +94,9 @@ class ExtensionManager(object):
def names(self):
"Returns the names of the discovered extensions"
# We want to return the names of the extensions in the order
# they would be used by map(), since some subclasses change
# that order.
return [e.name for e in self.extensions]
def map(self, func, *args, **kwds):
@ -133,3 +137,17 @@ class ExtensionManager(object):
def __iter__(self):
return iter(self.extensions)
def __getitem__(self, name):
"""Return the named extension.
Accessing an ExtensionManager as a dictionary (``em['name']``)
produces the :class:`Extension` instance with the
specified name.
"""
if self._extensions_by_name is None:
d = {}
for e in self.extensions:
d[e.name] = e
self._extensions_by_name = d
return self._extensions_by_name[name]

View File

@ -23,6 +23,7 @@ class HookManager(NamedExtensionManager):
def __init__(self, namespace, name,
invoke_on_load=False, invoke_args=(), invoke_kwds={}):
self._name = name
super(HookManager, self).__init__(
namespace,
[name],
@ -30,3 +31,14 @@ class HookManager(NamedExtensionManager):
invoke_args=invoke_args,
invoke_kwds=invoke_kwds,
)
def __getitem__(self, name):
"""Return the named extensions.
Accessing a HookManager as a dictionary (``em['name']``)
produces a list of the :class:`Extension` instance(s) with the
specified name, in the order they would be invoked by map().
"""
if name != self._name:
raise KeyError(name)
return self.extensions

View File

@ -18,6 +18,22 @@ def test_detect_plugins():
assert names == ['t1', 't2']
def test_get_by_name():
em = extension.ExtensionManager('stevedore.test.extension')
e = em['t1']
assert e.name == 't1'
def test_get_by_name_missing():
em = extension.ExtensionManager('stevedore.test.extension')
try:
em['t3']
except KeyError:
pass
else:
assert False, 'Failed to raise KeyError'
def test_load_multiple_times_entry_points():
# We expect to get the same EntryPoint object because we save them
# in the cache.

View File

@ -11,3 +11,33 @@ def test_hook():
)
assert len(em.extensions) == 1
assert em.names() == ['t1']
def test_get_by_name():
em = hook.HookManager(
'stevedore.test.extension',
't1',
invoke_on_load=True,
invoke_args=('a',),
invoke_kwds={'b': 'B'},
)
e_list = em['t1']
assert len(e_list) == 1
e = e_list[0]
assert e.name == 't1'
def test_get_by_name_missing():
em = hook.HookManager(
'stevedore.test.extension',
't1',
invoke_on_load=True,
invoke_args=('a',),
invoke_kwds={'b': 'B'},
)
try:
em['t2']
except KeyError:
pass
else:
assert False, 'Failed to raise KeyError'