From 820a592df74bf4b5f9b7f7feb5caba4b325a2f7e Mon Sep 17 00:00:00 2001 From: Stefano Borini <sborini@enthought.com> Date: Wed, 16 May 2018 15:15:41 +0100 Subject: [PATCH] Reports error in case of failed initialization of factory --- force_bdss/base_extension_plugin.py | 76 +++++++++++++++++-- .../probe_classes/probe_extension_plugin.py | 17 +++++ .../tests/test_base_extension_plugin.py | 20 +++++ 3 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 force_bdss/tests/probe_classes/probe_extension_plugin.py create mode 100644 force_bdss/tests/test_base_extension_plugin.py diff --git a/force_bdss/base_extension_plugin.py b/force_bdss/base_extension_plugin.py index baaeb42..7ba5490 100644 --- a/force_bdss/base_extension_plugin.py +++ b/force_bdss/base_extension_plugin.py @@ -1,6 +1,16 @@ +import logging +import traceback + from envisage.plugin import Plugin -from traits.trait_types import List +from traits.api import List, Unicode, Bool, Type, Either +from force_bdss.data_sources.base_data_source_factory import \ + BaseDataSourceFactory +from force_bdss.mco.base_mco_factory import BaseMCOFactory +from force_bdss.notification_listeners.base_notification_listener_factory \ + import \ + BaseNotificationListenerFactory +from force_bdss.ui_hooks.base_ui_hooks_factory import BaseUIHooksFactory from .notification_listeners.i_notification_listener_factory import \ INotificationListenerFactory from .ids import ExtensionPointID @@ -9,20 +19,40 @@ from .mco.i_mco_factory import IMCOFactory from .ui_hooks.i_ui_hooks_factory import IUIHooksFactory +logger = logging.getLogger(__name__) + + class BaseExtensionPlugin(Plugin): """Base class for extension plugins, that is, plugins that are provided by external contributors. It provides a set of slots to be populated that end up contributing to the application extension points. To use the class, simply inherit it - in your plugin, and then define the trait default initializer for the - specific trait you want to populate. For example:: + in your plugin, and then fill the factory_classes trait with the classes + you want to export. For example:: class MyPlugin(BaseExtensionPlugin): - def _data_source_factories_default(self): - return [MyDataSourceFactory1(), - MyDataSourceFactory2()] + id = plugin_id("enthought", "myplugin") + + factory_classes = [ + MyDataSourceFactory1, + MyDataSourceFactory2 + ] """ + #: Reports if the plugin loaded its factories successfully or not. + broken = Bool(False) + + #: The error that have been generated by the instantiations. + error = Unicode() + + #: A list of all the factory classes to export. + factory_classes = List( + Either(Type(BaseDataSourceFactory), + Type(BaseMCOFactory), + Type(BaseNotificationListenerFactory), + Type(BaseUIHooksFactory) + ) + ) #: A list of available Multi Criteria Optimizers this plugin exports. mco_factories = List( @@ -36,12 +66,46 @@ class BaseExtensionPlugin(Plugin): contributes_to=ExtensionPointID.DATA_SOURCE_FACTORIES ) + #: A list of the available notification listeners this plugin exports notification_listener_factories = List( INotificationListenerFactory, contributes_to=ExtensionPointID.NOTIFICATION_LISTENER_FACTORIES ) + #: A list of the available ui hooks this plugin exports ui_hooks_factories = List( IUIHooksFactory, contributes_to=ExtensionPointID.UI_HOOKS_FACTORIES ) + + def _data_source_factories_default(self): + return self._instantiate_factories(BaseDataSourceFactory) + + def _mco_factories_default(self): + return self._instantiate_factories(BaseMCOFactory) + + def _notification_listener_factories_default(self): + return self._instantiate_factories(BaseNotificationListenerFactory) + + def _ui_hooks_factories_default(self): + return self._instantiate_factories(BaseUIHooksFactory) + + def _instantiate_factories(self, type_): + if self.broken: + logger.error( + "Skipping instantiation of {} due to previous errors.".format( + type_.__name__)) + return [] + + try: + return [ + factory(self) + for factory in self._factory_by_type(type_) + ] + except Exception as e: + self.broken = True + self.error = traceback.format_exc() + logger.exception(e) + + def _factory_by_type(self, type_): + return [cls for cls in self.factory_classes if issubclass(cls, type_)] diff --git a/force_bdss/tests/probe_classes/probe_extension_plugin.py b/force_bdss/tests/probe_classes/probe_extension_plugin.py new file mode 100644 index 0000000..115e02a --- /dev/null +++ b/force_bdss/tests/probe_classes/probe_extension_plugin.py @@ -0,0 +1,17 @@ +from force_bdss.base_extension_plugin import BaseExtensionPlugin +from force_bdss.ids import plugin_id +from force_bdss.tests.probe_classes.data_source import ProbeDataSourceFactory +from force_bdss.tests.probe_classes.mco import ProbeMCOFactory +from force_bdss.tests.probe_classes.notification_listener import \ + ProbeNotificationListenerFactory +from force_bdss.tests.probe_classes.ui_hooks import ProbeUIHooksFactory + + +class ProbeExtensionPlugin(BaseExtensionPlugin): + id = plugin_id("enthought", "test") + factory_classes = [ + ProbeDataSourceFactory, + ProbeMCOFactory, + ProbeNotificationListenerFactory, + ProbeUIHooksFactory, + ] diff --git a/force_bdss/tests/test_base_extension_plugin.py b/force_bdss/tests/test_base_extension_plugin.py new file mode 100644 index 0000000..5053499 --- /dev/null +++ b/force_bdss/tests/test_base_extension_plugin.py @@ -0,0 +1,20 @@ +import unittest + +from force_bdss.tests.probe_classes.probe_extension_plugin import \ + ProbeExtensionPlugin + +try: + import mock +except ImportError: + from unittest import mock + + +class TestBaseExtensionPlugin(unittest.TestCase): + def test_basic_init(self): + plugin = ProbeExtensionPlugin() + self.assertEqual(len(plugin.data_source_factories), 1) + self.assertEqual(len(plugin.notification_listener_factories), 1) + self.assertEqual(len(plugin.mco_factories), 1) + self.assertEqual(len(plugin.ui_hooks_factories), 1) + self.assertFalse(plugin.broken) + self.assertEqual(plugin.error, "") -- GitLab