Allow a on_load_failure_callback to be provided
When entrypoints are loaded it is sometimes useful to be able to do more than LOG the failure that could be emitted from the plugin failing to be constructed, allowing the manager classes to be provided a callback that can be called when such extension fails to load is useful to track these types of failures. Change-Id: Idc7e55ade6b3f80c348123fbceea64425d7d3508
This commit is contained in:
parent
1ace033015
commit
3eccd4baad
|
@ -34,6 +34,7 @@ stevedore.example.formatter =
|
|||
stevedore.test.extension =
|
||||
t1 = stevedore.tests.test_extension:FauxExtension
|
||||
t2 = stevedore.tests.test_extension:FauxExtension
|
||||
e1 = stevedore.tests.test_extension:BrokenExtension
|
||||
|
||||
|
||||
[build_sphinx]
|
||||
|
|
|
@ -133,7 +133,8 @@ class NameDispatchExtensionManager(DispatchExtensionManager):
|
|||
|
||||
def __init__(self, namespace, check_func, invoke_on_load=False,
|
||||
invoke_args=(), invoke_kwds={},
|
||||
propagate_map_exceptions=False):
|
||||
propagate_map_exceptions=False,
|
||||
on_load_failure_callback=None):
|
||||
super(NameDispatchExtensionManager, self).__init__(
|
||||
namespace=namespace,
|
||||
check_func=check_func,
|
||||
|
@ -141,6 +142,7 @@ class NameDispatchExtensionManager(DispatchExtensionManager):
|
|||
invoke_args=invoke_args,
|
||||
invoke_kwds=invoke_kwds,
|
||||
propagate_map_exceptions=propagate_map_exceptions,
|
||||
on_load_failure_callback=on_load_failure_callback
|
||||
)
|
||||
|
||||
def _init_plugins(self, extensions):
|
||||
|
|
|
@ -22,18 +22,21 @@ class DriverManager(NamedExtensionManager):
|
|||
"""
|
||||
|
||||
def __init__(self, namespace, name,
|
||||
invoke_on_load=False, invoke_args=(), invoke_kwds={}):
|
||||
invoke_on_load=False, invoke_args=(), invoke_kwds={},
|
||||
on_load_failure_callback=None):
|
||||
super(DriverManager, self).__init__(
|
||||
namespace=namespace,
|
||||
names=[name],
|
||||
invoke_on_load=invoke_on_load,
|
||||
invoke_args=invoke_args,
|
||||
invoke_kwds=invoke_kwds,
|
||||
on_load_failure_callback=on_load_failure_callback
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def make_test_instance(cls, extension, namespace='TESTING',
|
||||
propagate_map_exceptions=False):
|
||||
propagate_map_exceptions=False,
|
||||
on_load_failure_callback=None):
|
||||
"""Construct a test DriverManager
|
||||
|
||||
Test instances are passed a list of extensions to work from rather
|
||||
|
@ -54,7 +57,8 @@ class DriverManager(NamedExtensionManager):
|
|||
|
||||
o = super(DriverManager, cls).make_test_instance(
|
||||
[extension], namespace=namespace,
|
||||
propagate_map_exceptions=propagate_map_exceptions)
|
||||
propagate_map_exceptions=propagate_map_exceptions,
|
||||
on_load_failure_callback=on_load_failure_callback)
|
||||
return o
|
||||
|
||||
def _init_plugins(self, extensions, propagate_map_exceptions=False):
|
||||
|
|
|
@ -37,7 +37,8 @@ class EnabledExtensionManager(ExtensionManager):
|
|||
|
||||
def __init__(self, namespace, check_func, invoke_on_load=False,
|
||||
invoke_args=(), invoke_kwds={},
|
||||
propagate_map_exceptions=False):
|
||||
propagate_map_exceptions=False,
|
||||
on_load_failure_callback=None):
|
||||
self.check_func = check_func
|
||||
super(EnabledExtensionManager, self).__init__(
|
||||
namespace,
|
||||
|
@ -45,6 +46,7 @@ class EnabledExtensionManager(ExtensionManager):
|
|||
invoke_args=invoke_args,
|
||||
invoke_kwds=invoke_kwds,
|
||||
propagate_map_exceptions=propagate_map_exceptions,
|
||||
on_load_failure_callback=on_load_failure_callback
|
||||
)
|
||||
|
||||
def _load_one_plugin(self, ep, invoke_on_load, invoke_args, invoke_kwds):
|
||||
|
|
|
@ -65,16 +65,23 @@ class ExtensionManager(object):
|
|||
are propagated up through the map call or whether they are logged and
|
||||
then ignored
|
||||
:type propagate_map_exceptions: bool
|
||||
|
||||
:param on_load_failure_callback: Callback function that will be called when
|
||||
a entrypoint can not be loaded. The arguments that will be provided
|
||||
when this is called (when an entrypoint fails to load) are
|
||||
(manager, entrypoint, exception)
|
||||
:type on_load_failure_callback: function
|
||||
"""
|
||||
|
||||
def __init__(self, namespace,
|
||||
invoke_on_load=False,
|
||||
invoke_args=(),
|
||||
invoke_kwds={},
|
||||
propagate_map_exceptions=False):
|
||||
propagate_map_exceptions=False,
|
||||
on_load_failure_callback=None):
|
||||
self._init_attributes(
|
||||
namespace, propagate_map_exceptions=propagate_map_exceptions)
|
||||
namespace,
|
||||
propagate_map_exceptions=propagate_map_exceptions,
|
||||
on_load_failure_callback=on_load_failure_callback)
|
||||
extensions = self._load_plugins(invoke_on_load,
|
||||
invoke_args,
|
||||
invoke_kwds)
|
||||
|
@ -82,7 +89,8 @@ class ExtensionManager(object):
|
|||
|
||||
@classmethod
|
||||
def make_test_instance(cls, extensions, namespace='TESTING',
|
||||
propagate_map_exceptions=False):
|
||||
propagate_map_exceptions=False,
|
||||
on_load_failure_callback=None):
|
||||
"""Construct a test ExtensionManager
|
||||
|
||||
Test instances are passed a list of extensions to work from rather
|
||||
|
@ -103,13 +111,16 @@ class ExtensionManager(object):
|
|||
|
||||
o = cls.__new__(cls)
|
||||
o._init_attributes(namespace,
|
||||
propagate_map_exceptions=propagate_map_exceptions)
|
||||
propagate_map_exceptions=propagate_map_exceptions,
|
||||
on_load_failure_callback=on_load_failure_callback)
|
||||
o._init_plugins(extensions)
|
||||
return o
|
||||
|
||||
def _init_attributes(self, namespace, propagate_map_exceptions=False):
|
||||
def _init_attributes(self, namespace, propagate_map_exceptions=False,
|
||||
on_load_failure_callback=None):
|
||||
self.namespace = namespace
|
||||
self.propagate_map_exceptions = propagate_map_exceptions
|
||||
self._on_load_failure_callback = on_load_failure_callback
|
||||
|
||||
def _init_plugins(self, extensions):
|
||||
self.extensions = extensions
|
||||
|
@ -140,6 +151,8 @@ class ExtensionManager(object):
|
|||
except Exception as err:
|
||||
LOG.error('Could not load %r: %s', ep.name, err)
|
||||
LOG.exception(err)
|
||||
if self._on_load_failure_callback is not None:
|
||||
self._on_load_failure_callback(self, ep, err)
|
||||
return extensions
|
||||
|
||||
def _load_one_plugin(self, ep, invoke_on_load, invoke_args, invoke_kwds):
|
||||
|
|
|
@ -22,20 +22,24 @@ class HookManager(NamedExtensionManager):
|
|||
"""
|
||||
|
||||
def __init__(self, namespace, name,
|
||||
invoke_on_load=False, invoke_args=(), invoke_kwds={}):
|
||||
invoke_on_load=False, invoke_args=(), invoke_kwds={},
|
||||
on_load_failure_callback=None):
|
||||
super(HookManager, self).__init__(
|
||||
namespace,
|
||||
[name],
|
||||
invoke_on_load=invoke_on_load,
|
||||
invoke_args=invoke_args,
|
||||
invoke_kwds=invoke_kwds,
|
||||
on_load_failure_callback=on_load_failure_callback
|
||||
)
|
||||
|
||||
def _init_attributes(self, namespace, names, name_order=False,
|
||||
propagate_map_exceptions=False):
|
||||
propagate_map_exceptions=False,
|
||||
on_load_failure_callback=None):
|
||||
super(HookManager, self)._init_attributes(
|
||||
namespace, names,
|
||||
propagate_map_exceptions=propagate_map_exceptions)
|
||||
propagate_map_exceptions=propagate_map_exceptions,
|
||||
on_load_failure_callback=on_load_failure_callback)
|
||||
self._name = names[0]
|
||||
|
||||
def __getitem__(self, name):
|
||||
|
|
|
@ -34,10 +34,12 @@ class NamedExtensionManager(ExtensionManager):
|
|||
|
||||
def __init__(self, namespace, names,
|
||||
invoke_on_load=False, invoke_args=(), invoke_kwds={},
|
||||
name_order=False, propagate_map_exceptions=False):
|
||||
name_order=False, propagate_map_exceptions=False,
|
||||
on_load_failure_callback=None):
|
||||
self._init_attributes(
|
||||
namespace, names, name_order=name_order,
|
||||
propagate_map_exceptions=propagate_map_exceptions)
|
||||
propagate_map_exceptions=propagate_map_exceptions,
|
||||
on_load_failure_callback=on_load_failure_callback)
|
||||
extensions = self._load_plugins(invoke_on_load,
|
||||
invoke_args,
|
||||
invoke_kwds)
|
||||
|
@ -45,7 +47,8 @@ class NamedExtensionManager(ExtensionManager):
|
|||
|
||||
@classmethod
|
||||
def make_test_instance(cls, extensions, namespace='TESTING',
|
||||
propagate_map_exceptions=False):
|
||||
propagate_map_exceptions=False,
|
||||
on_load_failure_callback=None):
|
||||
"""Construct a test NamedExtensionManager
|
||||
|
||||
Test instances are passed a list of extensions to use rather than
|
||||
|
@ -67,14 +70,17 @@ class NamedExtensionManager(ExtensionManager):
|
|||
o = cls.__new__(cls)
|
||||
names = [e.name for e in extensions]
|
||||
o._init_attributes(namespace, names,
|
||||
propagate_map_exceptions=propagate_map_exceptions)
|
||||
propagate_map_exceptions=propagate_map_exceptions,
|
||||
on_load_failure_callback=on_load_failure_callback)
|
||||
o._init_plugins(extensions)
|
||||
return o
|
||||
|
||||
def _init_attributes(self, namespace, names, name_order=False,
|
||||
propagate_map_exceptions=False):
|
||||
propagate_map_exceptions=False,
|
||||
on_load_failure_callback=None):
|
||||
super(NamedExtensionManager, self)._init_attributes(
|
||||
namespace, propagate_map_exceptions=propagate_map_exceptions)
|
||||
namespace, propagate_map_exceptions=propagate_map_exceptions,
|
||||
on_load_failure_callback=on_load_failure_callback)
|
||||
|
||||
self._names = names
|
||||
self._name_order = name_order
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
"""Tests for failure loading callback
|
||||
"""
|
||||
|
||||
from stevedore import extension
|
||||
|
||||
|
||||
def test_extension_failure_custom_callback():
|
||||
errors = []
|
||||
|
||||
def failure_callback(manager, entrypoint, error):
|
||||
errors.append((manager, entrypoint, error))
|
||||
|
||||
em = extension.ExtensionManager('stevedore.test.extension',
|
||||
invoke_on_load=True,
|
||||
on_load_failure_callback=failure_callback)
|
||||
extensions = list(em.extensions)
|
||||
assert len(extensions) > 0
|
||||
assert len(errors) == 1
|
||||
(manager, entrypoint, error) = errors[0]
|
||||
assert manager is em
|
||||
assert isinstance(error, IOError)
|
|
@ -5,6 +5,9 @@ import mock
|
|||
|
||||
from stevedore import extension
|
||||
|
||||
ALL_NAMES = ['e1', 't1', 't2']
|
||||
WORKING_NAMES = ['t1', 't2']
|
||||
|
||||
|
||||
class FauxExtension(object):
|
||||
def __init__(self, *args, **kwds):
|
||||
|
@ -15,10 +18,15 @@ class FauxExtension(object):
|
|||
return self.args, self.kwds, data
|
||||
|
||||
|
||||
class BrokenExtension(object):
|
||||
def __init__(self, *args, **kwds):
|
||||
raise IOError("Did not create")
|
||||
|
||||
|
||||
def test_detect_plugins():
|
||||
em = extension.ExtensionManager('stevedore.test.extension')
|
||||
names = sorted(em.names())
|
||||
assert names == ['t1', 't2']
|
||||
assert names == ALL_NAMES
|
||||
|
||||
|
||||
def test_get_by_name():
|
||||
|
@ -73,7 +81,7 @@ def test_use_cache():
|
|||
def test_iterable():
|
||||
em = extension.ExtensionManager('stevedore.test.extension')
|
||||
names = sorted(e.name for e in em)
|
||||
assert names == ['t1', 't2']
|
||||
assert names == ALL_NAMES
|
||||
|
||||
|
||||
def test_invoke_on_load():
|
||||
|
@ -96,7 +104,7 @@ def test_map_return_values():
|
|||
invoke_on_load=True,
|
||||
)
|
||||
results = em.map(mapped)
|
||||
assert sorted(results) == ['t1', 't2']
|
||||
assert sorted(results) == WORKING_NAMES
|
||||
|
||||
|
||||
def test_map_arguments():
|
||||
|
@ -111,7 +119,7 @@ def test_map_arguments():
|
|||
em.map(mapped, 1, 2, a='A', b='B')
|
||||
assert len(objs) == 2
|
||||
names = sorted([o[0].name for o in objs])
|
||||
assert names == ['t1', 't2']
|
||||
assert names == WORKING_NAMES
|
||||
for o in objs:
|
||||
assert o[1] == (1, 2)
|
||||
assert o[2] == {'a': 'A', 'b': 'B'}
|
||||
|
|
Loading…
Reference in New Issue