diff --git a/force_bdss/base_core_driver.py b/force_bdss/base_core_driver.py index b3c2702ef10a8e7750dd01fa9ced87a22e2c70c8..5875f1784dd0db5af54adb5b5901aeb869aca1a0 100644 --- a/force_bdss/base_core_driver.py +++ b/force_bdss/base_core_driver.py @@ -1,12 +1,10 @@ -from envisage.extension_point import ExtensionPoint from envisage.plugin import Plugin -from traits.api import List +from traits.trait_types import Instance -from force_bdss.data_sources.i_data_source_bundle import ( - IDataSourceBundle) -from force_bdss.kpi.i_kpi_calculator_bundle import IKPICalculatorBundle -from force_bdss.mco.i_multi_criteria_optimizer_bundle import ( - IMultiCriteriaOptimizerBundle) +from force_bdss.bundle_registry_plugin import ( + BundleRegistryPlugin, + BUNDLE_REGISTRY_PLUGIN_ID +) class BaseCoreDriver(Plugin): @@ -14,50 +12,7 @@ class BaseCoreDriver(Plugin): or the evaluation. """ - # Note: we are forced to declare these extensions points here instead - # of the application object, and this is why we have to use this plugin. - # It is a workaround to an envisage bug that does not find the extension - # points if declared on the application. + bundle_registry = Instance(BundleRegistryPlugin) - #: A List of the available Multi Criteria Optimizers. - #: This will be populated by MCO plugins. - mco_bundles = ExtensionPoint( - List(IMultiCriteriaOptimizerBundle), - id='force.bdss.mco.bundles') - - #: A list of the available Data Sources. - #: It will be populated by plugins. - data_source_bundles = ExtensionPoint( - List(IDataSourceBundle), - id='force.bdss.data_sources.bundles') - - #: A list of the available Key Performance Indicator calculators. - #: It will be populated by plugins. - kpi_calculator_bundles = ExtensionPoint( - List(IKPICalculatorBundle), - id='force.bdss.kpi_calculators.bundles') - - def _data_source_bundle_by_id(self, id): - for ds in self.data_source_bundles: - if ds.id == id: - return ds - - raise Exception("Requested data source {} but don't know " - "to find it.".format(id)) - - def _kpi_calculator_bundle_by_id(self, id): - for kpic in self.kpi_calculator_bundles: - if kpic.id == id: - return kpic - - raise Exception( - "Requested kpi calculator {} but don't know " - "to find it.".format(id)) - - def _mco_bundle_by_id(self, id): - for mco in self.mco_bundles: - if mco.id == id: - return mco - - raise Exception("Requested MCO {} but it's not available" - "to compute it.".format(id)) + def _bundle_registry_default(self): + return self.application.get_plugin(BUNDLE_REGISTRY_PLUGIN_ID) diff --git a/force_bdss/bdss_application.py b/force_bdss/bdss_application.py index a0ac1141945a0ef434a55592bed537bd5011a163..c813e4dc4357c922bc356ef48f076adfc70966be 100644 --- a/force_bdss/bdss_application.py +++ b/force_bdss/bdss_application.py @@ -5,6 +5,7 @@ from stevedore.exception import NoMatches from envisage.api import Application from envisage.core_plugin import CorePlugin +from force_bdss.bundle_registry_plugin import BundleRegistryPlugin from force_bdss.core_evaluation_driver import CoreEvaluationDriver from force_bdss.core_mco_driver import CoreMCODriver @@ -33,7 +34,7 @@ class BDSSApplication(Application): self.evaluate = evaluate self.workflow_filepath = workflow_filepath - plugins = [CorePlugin()] + plugins = [CorePlugin(), BundleRegistryPlugin()] if self.evaluate: plugins.append(CoreEvaluationDriver()) diff --git a/force_bdss/bundle_registry_plugin.py b/force_bdss/bundle_registry_plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..f0e418c72e931bc49bf6600dac65be56dcdde7d0 --- /dev/null +++ b/force_bdss/bundle_registry_plugin.py @@ -0,0 +1,107 @@ +from envisage.extension_point import ExtensionPoint +from envisage.plugin import Plugin +from traits.api import List + +from force_bdss.data_sources.i_data_source_bundle import ( + IDataSourceBundle) +from force_bdss.kpi.i_kpi_calculator_bundle import IKPICalculatorBundle +from force_bdss.mco.i_multi_criteria_optimizer_bundle import ( + IMultiCriteriaOptimizerBundle) + + +BUNDLE_REGISTRY_PLUGIN_ID = "force.bdss.plugins.bundle_registry" + + +class BundleRegistryPlugin(Plugin): + """Main plugin that handles the execution of the MCO + or the evaluation. + """ + id = BUNDLE_REGISTRY_PLUGIN_ID + + # Note: we are forced to declare these extensions points here instead + # of the application object, and this is why we have to use this plugin. + # It is a workaround to an envisage bug that does not find the extension + # points if declared on the application. + + #: A List of the available Multi Criteria Optimizers. + #: This will be populated by MCO plugins. + mco_bundles = ExtensionPoint( + List(IMultiCriteriaOptimizerBundle), + id='force.bdss.mco.bundles') + + #: A list of the available Data Sources. + #: It will be populated by plugins. + data_source_bundles = ExtensionPoint( + List(IDataSourceBundle), + id='force.bdss.data_sources.bundles') + + #: A list of the available Key Performance Indicator calculators. + #: It will be populated by plugins. + kpi_calculator_bundles = ExtensionPoint( + List(IKPICalculatorBundle), + id='force.bdss.kpi_calculators.bundles') + + def data_source_bundle_by_id(self, id): + """Finds a given data source bundle by means of its id. + The ID is as obtained by the function bundle_id() in the + plugin api. + + Parameters + ---------- + id: str + The identifier returned by the bundle_id() function. + + Raises + ------ + ValueError: if the entry is not found. + """ + for ds in self.data_source_bundles: + if ds.id == id: + return ds + + raise ValueError( + "Requested data source {} but don't know how " + "to find it.".format(id)) + + def kpi_calculator_bundle_by_id(self, id): + """Finds a given kpi bundle by means of its id. + The ID is as obtained by the function bundle_id() in the + plugin api. + + Parameters + ---------- + id: str + The identifier returned by the bundle_id() function. + + Raises + ------ + ValueError: if the entry is not found. + """ + for kpic in self.kpi_calculator_bundles: + if kpic.id == id: + return kpic + + raise ValueError( + "Requested kpi calculator {} but don't know how " + "to find it.".format(id)) + + def mco_bundle_by_id(self, id): + """Finds a given Multi Criteria Optimizer (MCO) bundle by means of + its id. The ID is as obtained by the function bundle_id() in the + plugin api. + + Parameters + ---------- + id: str + The identifier returned by the bundle_id() function. + + Raises + ------ + ValueError: if the entry is not found. + """ + for mco in self.mco_bundles: + if mco.id == id: + return mco + + raise ValueError("Requested MCO {} but don't know how " + "to find it.".format(id)) diff --git a/force_bdss/core_evaluation_driver.py b/force_bdss/core_evaluation_driver.py index bf6c848c7b0acf3c400601eaaf19e1c167788bdd..19e56664c580721246ee42ad65197512ccad8e0a 100644 --- a/force_bdss/core_evaluation_driver.py +++ b/force_bdss/core_evaluation_driver.py @@ -13,7 +13,7 @@ class CoreEvaluationDriver(BaseCoreDriver): workflow = self.application.workflow mco_data = workflow.multi_criteria_optimizer - mco_bundle = self._mco_bundle_by_id(mco_data.id) + mco_bundle = self.bundle_registry.mco_bundle_by_id(mco_data.id) mco_model = mco_bundle.create_model(mco_data.model_data) mco_communicator = mco_bundle.create_communicator( self.application, @@ -23,7 +23,7 @@ class CoreEvaluationDriver(BaseCoreDriver): ds_results = [] for requested_ds in workflow.data_sources: - ds_bundle = self._data_source_bundle_by_id( + ds_bundle = self.bundle_registry.data_source_bundle_by_id( requested_ds.id) ds_model = ds_bundle.create_model(requested_ds.model_data) data_source = ds_bundle.create_data_source( @@ -32,7 +32,7 @@ class CoreEvaluationDriver(BaseCoreDriver): kpi_results = [] for requested_kpic in workflow.kpi_calculators: - kpic_bundle = self._kpi_calculator_bundle_by_id( + kpic_bundle = self.bundle_registry.kpi_calculator_bundle_by_id( requested_kpic.id) ds_model = kpic_bundle.create_model( requested_kpic.model_data) diff --git a/force_bdss/core_mco_driver.py b/force_bdss/core_mco_driver.py index f1cb9b483ac0727f6afcb813d4f0c0c933db836e..6a1321e84587e9bc681dff92d529a3340ab728fb 100644 --- a/force_bdss/core_mco_driver.py +++ b/force_bdss/core_mco_driver.py @@ -13,7 +13,7 @@ class CoreMCODriver(BaseCoreDriver): workflow = self.application.workflow mco_data = workflow.multi_criteria_optimizer - mco_bundle = self._mco_bundle_by_id(mco_data.id) + mco_bundle = self.bundle_registry.mco_bundle_by_id(mco_data.id) mco_model = mco_bundle.create_model(mco_data.model_data) mco = mco_bundle.create_optimizer(self.application, mco_model) diff --git a/force_bdss/tests/test_bundle_registry_plugin.py b/force_bdss/tests/test_bundle_registry_plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..f0869f0dc518d1dd26d6e9fd7d4947b8d0209be3 --- /dev/null +++ b/force_bdss/tests/test_bundle_registry_plugin.py @@ -0,0 +1,99 @@ +import unittest + +from force_bdss.id_generators import bundle_id + +try: + import mock +except ImportError: + from unittest import mock + +from traits.api import List +from envisage.application import Application +from envisage.plugin import Plugin + +from force_bdss.bundle_registry_plugin import BundleRegistryPlugin +from force_bdss.data_sources.i_data_source_bundle import IDataSourceBundle +from force_bdss.kpi.i_kpi_calculator_bundle import IKPICalculatorBundle +from force_bdss.mco.i_multi_criteria_optimizer_bundle import \ + IMultiCriteriaOptimizerBundle + + +class TestBundleRegistry(unittest.TestCase): + def setUp(self): + self.plugin = BundleRegistryPlugin() + self.app = Application([self.plugin]) + self.app.start() + self.app.stop() + + def test_initialization(self): + self.assertEqual(self.plugin.mco_bundles, []) + self.assertEqual(self.plugin.data_source_bundles, []) + self.assertEqual(self.plugin.kpi_calculator_bundles, []) + + +class BaseBDSSPlugin(Plugin): + mco_bundles = List( + IMultiCriteriaOptimizerBundle, + contributes_to='force.bdss.mco.bundles' + ) + + #: A list of the available Data Sources. + #: It will be populated by plugins. + data_source_bundles = List( + IDataSourceBundle, + contributes_to='force.bdss.data_sources.bundles') + + kpi_calculator_bundles = List( + IKPICalculatorBundle, + contributes_to='force.bdss.kpi_calculators.bundles' + ) + + +class MySuperPlugin(BaseBDSSPlugin): + def _mco_bundles_default(self): + return [mock.Mock(spec=IMultiCriteriaOptimizerBundle, + id=bundle_id("enthought", "mco1"))] + + def _data_source_bundles_default(self): + return [mock.Mock(spec=IDataSourceBundle, + id=bundle_id("enthought", "ds1")), + mock.Mock(spec=IDataSourceBundle, + id=bundle_id("enthought", "ds2"))] + + def _kpi_calculator_bundles_default(self): + return [mock.Mock(spec=IKPICalculatorBundle, + id=bundle_id("enthought", "kpi1")), + mock.Mock(spec=IKPICalculatorBundle, + id=bundle_id("enthought", "kpi2")), + mock.Mock(spec=IKPICalculatorBundle, + id=bundle_id("enthought", "kpi3"))] + + +class TestBundleRegistryWithContent(unittest.TestCase): + def setUp(self): + self.plugin = BundleRegistryPlugin() + self.app = Application([self.plugin, MySuperPlugin()]) + self.app.start() + self.app.stop() + + def test_initialization(self): + self.assertEqual(len(self.plugin.mco_bundles), 1) + self.assertEqual(len(self.plugin.data_source_bundles), 2) + self.assertEqual(len(self.plugin.kpi_calculator_bundles), 3) + + def test_lookup(self): + id = bundle_id("enthought", "mco1") + self.assertEqual(self.plugin.mco_bundle_by_id(id).id, id) + + for entry in ["ds1", "ds2"]: + id = bundle_id("enthought", entry) + self.assertEqual(self.plugin.data_source_bundle_by_id(id).id, id) + + for entry in ["kpi1", "kpi2", "kpi3"]: + id = bundle_id("enthought", entry) + self.assertEqual(self.plugin.kpi_calculator_bundle_by_id(id).id, + id) + + +if __name__ == '__main__': + unittest.main()