Skip to content
Snippets Groups Projects
Commit 284b66e5 authored by Stefano Borini's avatar Stefano Borini
Browse files

Changed API to accomodate stricter control over incorrect setup of plugins

parent 820a592d
No related branches found
No related tags found
1 merge request!129Safer plugin import
This commit is part of merge request !129. Comments created here will be created in the context of that merge request.
......@@ -13,7 +13,7 @@ from force_bdss.notification_listeners.base_notification_listener_factory \
from force_bdss.ui_hooks.base_ui_hooks_factory import BaseUIHooksFactory
from .notification_listeners.i_notification_listener_factory import \
INotificationListenerFactory
from .ids import ExtensionPointID
from .ids import ExtensionPointID, plugin_id
from .data_sources.i_data_source_factory import IDataSourceFactory
from .mco.i_mco_factory import IMCOFactory
from .ui_hooks.i_ui_hooks_factory import IUIHooksFactory
......@@ -28,15 +28,20 @@ class BaseExtensionPlugin(Plugin):
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 fill the factory_classes trait with the classes
you want to export. For example::
in your plugin, and reimplement the methods as from example::
class MyPlugin(BaseExtensionPlugin):
id = plugin_id("enthought", "myplugin")
def get_producer(self):
return "enthought"
factory_classes = [
MyDataSourceFactory1,
MyDataSourceFactory2
def get_identifier(self):
return "myplugin"
def get_factory_classes(self):
return [
MyDataSourceFactory1,
MyDataSourceFactory2,
MyMCOFactory
]
"""
#: Reports if the plugin loaded its factories successfully or not.
......@@ -78,34 +83,62 @@ class BaseExtensionPlugin(Plugin):
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 []
def __init__(self, *args, **kwargs):
super(BaseExtensionPlugin, self).__init__(*args, **kwargs)
try:
return [
factory(self)
for factory in self._factory_by_type(type_)
self.id = plugin_id(self.get_producer(), self.get_identifier())
self.factory_classes = self.get_factory_classes()
self.mco_factories[:] = [
cls(self)
for cls in self._factory_by_type(BaseMCOFactory)]
self.data_source_factories[:] = [
cls(self)
for cls in self._factory_by_type(BaseDataSourceFactory)]
self.notification_listener_factories[:] = [
cls(self)
for cls in self._factory_by_type(
BaseNotificationListenerFactory)
]
self.ui_hooks_factories[:] = [
cls(self)
for cls in self._factory_by_type(
BaseUIHooksFactory)
]
except Exception as e:
self.broken = True
self.error = traceback.format_exc()
logger.exception(e)
self.broken = True
self.mco_factories[:] = []
self.data_source_factories[:] = []
self.notification_listener_factories[:] = []
self.ui_hooks_factories[:] = []
def get_producer(self):
"""Must be reimplemented to return a string with the name of the
company producing this plugin. Examples are "enthought", "itwm" etc.
"""
raise NotImplementedError(
"get_producer was not implemented in plugin {}".format(
self.__class__))
def get_identifier(self):
"""Must return a string with the name of the plugin the producer
is releasing. The name must be unique and is responsibility of
the producer to guarantee this name is not conflicting with
another already existing plugin
"""
raise NotImplementedError(
"get_identifier was not implemented in plugin {}".format(
self.__class__))
def get_factory_classes(self):
"""Must return a list of factory classes that this plugin exports.
"""
raise NotImplementedError(
"get_factory_classes was not implemented in plugin {}".format(
self.__class__))
def _factory_by_type(self, type_):
return [cls for cls in self.factory_classes if issubclass(cls, type_)]
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 \
......@@ -8,10 +7,16 @@ from force_bdss.tests.probe_classes.ui_hooks import ProbeUIHooksFactory
class ProbeExtensionPlugin(BaseExtensionPlugin):
id = plugin_id("enthought", "test")
factory_classes = [
ProbeDataSourceFactory,
ProbeMCOFactory,
ProbeNotificationListenerFactory,
ProbeUIHooksFactory,
]
def get_producer(self):
return "enthought"
def get_identifier(self):
return "test"
def get_factory_classes(self):
return [
ProbeDataSourceFactory,
ProbeMCOFactory,
ProbeNotificationListenerFactory,
ProbeUIHooksFactory,
]
......@@ -3,9 +3,15 @@ import warnings
from force_bdss.base_extension_plugin import (
BaseExtensionPlugin)
from force_bdss.data_sources.base_data_source_factory import \
BaseDataSourceFactory
from force_bdss.ids import factory_id, mco_parameter_id
from force_bdss.mco.base_mco_factory import BaseMCOFactory
from force_bdss.mco.parameters.base_mco_parameter_factory import \
BaseMCOParameterFactory
from force_bdss.notification_listeners.base_notification_listener_factory \
import \
BaseNotificationListenerFactory
from force_bdss.notification_listeners.i_notification_listener_factory import \
INotificationListenerFactory
......@@ -33,29 +39,44 @@ class TestFactoryRegistry(unittest.TestCase):
self.assertEqual(self.plugin.data_source_factories, [])
class MySuperPlugin(BaseExtensionPlugin):
def _mco_factories_default(self):
class MCOFactory(BaseMCOFactory):
id = factory_id("enthought", "mco1")
def parameter_factories(self):
return [
mock.Mock(
spec=IMCOFactory,
id=factory_id("enthought", "mco1"),
parameter_factories=mock.Mock(return_value=[
mock.Mock(
spec=BaseMCOParameterFactory,
id=mco_parameter_id("enthought", "mco1", "ranged")
)
]),
)]
def _data_source_factories_default(self):
return [mock.Mock(spec=IDataSourceFactory,
id=factory_id("enthought", "ds1")),
mock.Mock(spec=IDataSourceFactory,
id=factory_id("enthought", "ds2"))]
def _notification_listener_factories_default(self):
return [mock.Mock(spec=INotificationListenerFactory,
id=factory_id("enthought", "nl1"))]
spec=BaseMCOParameterFactory,
id=mco_parameter_id("enthought", "mco1", "ranged")
)
]
class DataSourceFactory1(BaseDataSourceFactory):
id = factory_id("enthought", "ds1")
class DataSourceFactory2(BaseDataSourceFactory):
id = factory_id("enthought", "ds2")
class NotificationListenerFactory(BaseNotificationListenerFactory):
id = factory_id("enthought", "nl1")
class MySuperPlugin(BaseExtensionPlugin):
def get_producer(self):
return "enthought"
def get_identifier(self):
return "test"
def get_factory_classes(self):
return [
MCOFactory,
DataSourceFactory1,
DataSourceFactory2,
NotificationListenerFactory
]
class TestFactoryRegistryWithContent(unittest.TestCase):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment