diff --git a/force_bdss/base_core_driver.py b/force_bdss/base_core_driver.py index 7f0fa6db586df4ecf71a8a388cc746f910d87a02..b3c2702ef10a8e7750dd01fa9ced87a22e2c70c8 100644 --- a/force_bdss/base_core_driver.py +++ b/force_bdss/base_core_driver.py @@ -37,27 +37,27 @@ class BaseCoreDriver(Plugin): List(IKPICalculatorBundle), id='force.bdss.kpi_calculators.bundles') - def _data_source_bundle_by_name(self, name): + def _data_source_bundle_by_id(self, id): for ds in self.data_source_bundles: - if ds.name == name: + if ds.id == id: return ds raise Exception("Requested data source {} but don't know " - "to find it.".format(name)) + "to find it.".format(id)) - def _kpi_calculator_bundle_by_name(self, name): + def _kpi_calculator_bundle_by_id(self, id): for kpic in self.kpi_calculator_bundles: - if kpic.name == name: + if kpic.id == id: return kpic raise Exception( "Requested kpi calculator {} but don't know " - "to find it.".format(name)) + "to find it.".format(id)) - def _mco_bundle_by_name(self, name): + def _mco_bundle_by_id(self, id): for mco in self.mco_bundles: - if mco.name == name: + if mco.id == id: return mco raise Exception("Requested MCO {} but it's not available" - "to compute it.".format(name)) + "to compute it.".format(id)) diff --git a/force_bdss/bdss_application.py b/force_bdss/bdss_application.py index 64537cfa1a72ed6ab9a6db1aca9a99ca48a54342..a0ac1141945a0ef434a55592bed537bd5011a163 100644 --- a/force_bdss/bdss_application.py +++ b/force_bdss/bdss_application.py @@ -54,7 +54,7 @@ class BDSSApplication(Application): except NoMatches: print("No extensions found") - super().__init__(plugins=plugins) + super(BDSSApplication, self).__init__(plugins=plugins) def _workflow_default(self): with open(self.workflow_filepath) as f: diff --git a/force_bdss/cli/force_bdss.py b/force_bdss/cli/force_bdss.py index 32807502939a5bf92e55d58c4ce6d4dd4873b081..48ce95353a49a5531c68bd09d4e441c7151179d7 100644 --- a/force_bdss/cli/force_bdss.py +++ b/force_bdss/cli/force_bdss.py @@ -2,6 +2,11 @@ import click from ..bdss_application import BDSSApplication +# Makes the application rethrow the exception so that it exits return code +# different from zero. +from traits.api import push_exception_handler +push_exception_handler(reraise_exceptions=True) + @click.command() @click.option("--evaluate", is_flag=True) diff --git a/force_bdss/cli/tests/__init__.py b/force_bdss/cli/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/examples/foo.csv b/force_bdss/cli/tests/fixtures/foo.csv similarity index 100% rename from examples/foo.csv rename to force_bdss/cli/tests/fixtures/foo.csv diff --git a/examples/test_csv.json b/force_bdss/cli/tests/fixtures/test_csv.json similarity index 71% rename from examples/test_csv.json rename to force_bdss/cli/tests/fixtures/test_csv.json index 0fe50484a4c12813bcb882f6ea2d865aa9aac6bb..80437d0abf8ad92fd7d5529f3ab36fd5c614094e 100644 --- a/examples/test_csv.json +++ b/force_bdss/cli/tests/fixtures/test_csv.json @@ -1,13 +1,13 @@ { "multi_criteria_optimizer": { - "name": "dakota", + "id": "force.bdss.bundles.enthought.dakota", "model_data": { "value_types": ["DUMMY"] } }, "data_sources": [ { - "name": "csv_extractor", + "id": "force.bdss.bundles.enthought.csv_extractor", "model_data": { "filename": "foo.csv", "row": 3, @@ -16,7 +16,7 @@ } }, { - "name": "csv_extractor", + "id": "force.bdss.bundles.enthought.csv_extractor", "model_data": { "filename": "foo.csv", "row": 3, @@ -27,7 +27,7 @@ ], "kpi_calculators": [ { - "name": "kpi_adder", + "id": "force.bdss.bundles.enthought.kpi_adder", "model_data": { "cuba_type_in": "PRESSURE", "cuba_type_out": "TOTAL_PRESSURE" diff --git a/force_bdss/cli/tests/test_execution.py b/force_bdss/cli/tests/test_execution.py new file mode 100644 index 0000000000000000000000000000000000000000..3e4d28d928a9847fa8daa58aec65a4576ab2cc2c --- /dev/null +++ b/force_bdss/cli/tests/test_execution.py @@ -0,0 +1,31 @@ +import unittest +import subprocess +import os +from contextlib import contextmanager + + +@contextmanager +def cd(dir): + cwd = os.curdir + os.chdir(dir) + try: + yield + finally: + os.chdir(cwd) + + +def fixture_dir(): + return os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "fixtures") + + +class TestExecution(unittest.TestCase): + def test_plain_invocation_mco(self): + with cd(fixture_dir()): + out = subprocess.check_call(["force_bdss", "test_csv.json"]) + self.assertEqual(out, 0) + + +if __name__ == '__main__': + unittest.main() diff --git a/force_bdss/core_evaluation_driver.py b/force_bdss/core_evaluation_driver.py index 831c539cf39ba31b68a97ea21c6f3eb43ad8cc32..bf6c848c7b0acf3c400601eaaf19e1c167788bdd 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_name(mco_data.name) + mco_bundle = self._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,8 +23,8 @@ class CoreEvaluationDriver(BaseCoreDriver): ds_results = [] for requested_ds in workflow.data_sources: - ds_bundle = self._data_source_bundle_by_name( - requested_ds.name) + ds_bundle = self._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( self.application, ds_model) @@ -32,8 +32,8 @@ class CoreEvaluationDriver(BaseCoreDriver): kpi_results = [] for requested_kpic in workflow.kpi_calculators: - kpic_bundle = self._kpi_calculator_bundle_by_name( - requested_kpic.name) + kpic_bundle = self._kpi_calculator_bundle_by_id( + requested_kpic.id) ds_model = kpic_bundle.create_model( requested_kpic.model_data) kpi_calculator = kpic_bundle.create_data_source( diff --git a/force_bdss/core_mco_driver.py b/force_bdss/core_mco_driver.py index cc296dae4a20930f41d0d5a54755965db848661e..f1cb9b483ac0727f6afcb813d4f0c0c933db836e 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_name(mco_data.name) + mco_bundle = self._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/core_plugins/csv_extractor/csv_extractor/csv_extractor_bundle.py b/force_bdss/core_plugins/csv_extractor/csv_extractor/csv_extractor_bundle.py index b0a2e474c9bd49ca1aa6b224c555dfd9e742c703..295c44dc306b3816569845dcff7c2228f0c2b28e 100644 --- a/force_bdss/core_plugins/csv_extractor/csv_extractor/csv_extractor_bundle.py +++ b/force_bdss/core_plugins/csv_extractor/csv_extractor/csv_extractor_bundle.py @@ -2,6 +2,7 @@ from traits.api import provides, HasStrictTraits from traits.trait_types import String from force_bdss.data_sources.i_data_source_bundle import IDataSourceBundle +from force_bdss.id_generators import bundle_id from .csv_extractor_model import CSVExtractorModel from .csv_extractor_data_source import CSVExtractorDataSource @@ -9,7 +10,7 @@ from .csv_extractor_data_source import CSVExtractorDataSource @provides(IDataSourceBundle) class CSVExtractorBundle(HasStrictTraits): - name = String("csv_extractor") + id = String(bundle_id("enthought", "csv_extractor")) def create_model(self, model_data=None): if model_data is None: diff --git a/force_bdss/core_plugins/csv_extractor/csv_extractor_plugin.py b/force_bdss/core_plugins/csv_extractor/csv_extractor_plugin.py index 8f10341086f0a315270f63734855aa8ad4e18289..2dcba7e3bfd078a88ba46a0f2d70b0768ef5801a 100644 --- a/force_bdss/core_plugins/csv_extractor/csv_extractor_plugin.py +++ b/force_bdss/core_plugins/csv_extractor/csv_extractor_plugin.py @@ -7,8 +7,6 @@ from .csv_extractor.csv_extractor_bundle import CSVExtractorBundle class CSVExtractorPlugin(Plugin): - id = "force.bdss.data_sources.csv_extractor" - data_sources = List( IDataSourceBundle, contributes_to='force.bdss.data_sources.bundles' diff --git a/force_bdss/core_plugins/test_kpi/kpi_adder/kpi_adder_bundle.py b/force_bdss/core_plugins/test_kpi/kpi_adder/kpi_adder_bundle.py index 336cb374008c189534801ee93f9b74ede066abd4..e23e441e51d15344ab50d6a7ebdf2498aca2475e 100644 --- a/force_bdss/core_plugins/test_kpi/kpi_adder/kpi_adder_bundle.py +++ b/force_bdss/core_plugins/test_kpi/kpi_adder/kpi_adder_bundle.py @@ -1,6 +1,7 @@ from traits.api import provides, HasStrictTraits from traits.trait_types import String +from force_bdss.id_generators import bundle_id from force_bdss.kpi.i_kpi_calculator_bundle import IKPICalculatorBundle from .kpi_adder_model import KPIAdderModel @@ -9,7 +10,7 @@ from .kpi_adder_calculator import KPIAdderCalculator @provides(IKPICalculatorBundle) class KPIAdderBundle(HasStrictTraits): - name = String("kpi_adder") + id = String(bundle_id("enthought", "kpi_adder")) def create_model(self, model_data=None): if model_data is None: diff --git a/force_bdss/core_plugins/test_kpi/test_kpi_calculator_plugin.py b/force_bdss/core_plugins/test_kpi/test_kpi_calculator_plugin.py index 3be48fb51abc72a16d7a2fb8d40ff16b27e2207c..5250693b5e1ca20905d1185089fdaa8dbe524537 100644 --- a/force_bdss/core_plugins/test_kpi/test_kpi_calculator_plugin.py +++ b/force_bdss/core_plugins/test_kpi/test_kpi_calculator_plugin.py @@ -8,8 +8,6 @@ from .kpi_adder.kpi_adder_bundle import KPIAdderBundle class TestKPICalculatorPlugin(Plugin): - id = "force.bdss.kpi_calculators.test_kpi_calculator_plugin" - kpi_calculators = List( IKPICalculatorBundle, contributes_to='force.bdss.kpi_calculators.bundles' diff --git a/force_bdss/core_plugins/test_mco/dakota/dakota_bundle.py b/force_bdss/core_plugins/test_mco/dakota/dakota_bundle.py index 3d534742f15abd2686f68a5752ec09cf9afcc725..0e376ed702075b8da638fbc76708a08863247622 100644 --- a/force_bdss/core_plugins/test_mco/dakota/dakota_bundle.py +++ b/force_bdss/core_plugins/test_mco/dakota/dakota_bundle.py @@ -3,6 +3,7 @@ from traits.trait_types import String from force_bdss.core_plugins.test_mco.dakota.dakota_communicator import \ DakotaCommunicator +from force_bdss.id_generators import bundle_id from force_bdss.mco.i_multi_criteria_optimizer_bundle import ( IMultiCriteriaOptimizerBundle) @@ -12,7 +13,7 @@ from .dakota_optimizer import DakotaOptimizer @provides(IMultiCriteriaOptimizerBundle) class DakotaBundle(HasStrictTraits): - name = String("dakota") + id = String(bundle_id("enthought", "dakota")) def create_model(self, model_data=None): if model_data is None: diff --git a/force_bdss/core_plugins/test_mco/multi_criteria_optimizers_plugin.py b/force_bdss/core_plugins/test_mco/multi_criteria_optimizers_plugin.py index a71da0114b74f46e60f2c400a090fd51ccfa4f98..7805023cf713beae114728718e82f23b96c96552 100644 --- a/force_bdss/core_plugins/test_mco/multi_criteria_optimizers_plugin.py +++ b/force_bdss/core_plugins/test_mco/multi_criteria_optimizers_plugin.py @@ -8,8 +8,6 @@ from .dakota.dakota_bundle import DakotaBundle class MultiCriteriaOptimizersPlugin(Plugin): - id = "force.bdss.mco.plugins.multi_criteria_optimizers_plugin" - multi_criteria_optimizers = List( IMultiCriteriaOptimizerBundle, contributes_to='force.bdss.mco.bundles' diff --git a/force_bdss/data_sources/i_data_source_bundle.py b/force_bdss/data_sources/i_data_source_bundle.py index 5db54c836efe5da59391eb4db5711f7dad973913..146c1e8cbaa03be765ad68d2707411b58046824c 100644 --- a/force_bdss/data_sources/i_data_source_bundle.py +++ b/force_bdss/data_sources/i_data_source_bundle.py @@ -2,6 +2,11 @@ from traits.api import Interface, String class IDataSourceBundle(Interface): + #: Unique identifier that identifies the bundle uniquely in the + #: universe of bundles. Create one with the function bundle_id() + id = String() + + #: A human readable name of the bundle name = String() def create_data_source(self, application, model): diff --git a/force_bdss/id_generators.py b/force_bdss/id_generators.py new file mode 100644 index 0000000000000000000000000000000000000000..d89eca0783ff33d4c0e1050dc59967b2771629d8 --- /dev/null +++ b/force_bdss/id_generators.py @@ -0,0 +1,28 @@ +import six + + +def bundle_id(producer, identifier): + """Creates an id for the bundle. + + Parameters + ---------- + producer: str + the company or research institute unique identifier (e.g. "enthought") + identifier: str + A unique identifier for the bundle. The producer has authority and + control over the uniqueness of this identifier. + + Returns + ------- + str: an identifier to be used in the bundle. + """ + def is_valid(entry): + return ( + isinstance(entry, six.string_types) and + " " not in entry and + len(entry) != 0) + + if not all(map(is_valid, [producer, identifier])): + raise ValueError("Invalid parameters specified.") + + return "force.bdss.bundles.{}.{}".format(producer, identifier) diff --git a/force_bdss/kpi/i_kpi_calculator_bundle.py b/force_bdss/kpi/i_kpi_calculator_bundle.py index 42f780e5efff2c7f7908d79dc149894215bc143c..d4e8992ba059883f1b8824eb74966f3cdb0222e3 100644 --- a/force_bdss/kpi/i_kpi_calculator_bundle.py +++ b/force_bdss/kpi/i_kpi_calculator_bundle.py @@ -2,7 +2,7 @@ from traits.api import Interface, String class IKPICalculatorBundle(Interface): - name = String() + id = String() def create_kpi_calculator(self, application, model): pass diff --git a/force_bdss/mco/i_multi_criteria_optimizer_bundle.py b/force_bdss/mco/i_multi_criteria_optimizer_bundle.py index 69fe7a79a139ffbb48f71f0cd7f99d4cec8813ac..cb4222992cf0d7349a798e9747511c7c7e73b4d6 100644 --- a/force_bdss/mco/i_multi_criteria_optimizer_bundle.py +++ b/force_bdss/mco/i_multi_criteria_optimizer_bundle.py @@ -2,7 +2,7 @@ from traits.api import Interface, String class IMultiCriteriaOptimizerBundle(Interface): - name = String() + id = String() def create_optimizer(self, application, model): pass diff --git a/force_bdss/tests/__init__.py b/force_bdss/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/force_bdss/tests/test_id_generators.py b/force_bdss/tests/test_id_generators.py new file mode 100644 index 0000000000000000000000000000000000000000..85d240aaada5511a3de3f775e0be42352d3fbe14 --- /dev/null +++ b/force_bdss/tests/test_id_generators.py @@ -0,0 +1,15 @@ +import unittest + +from force_bdss.id_generators import bundle_id + + +class TestIdGenerators(unittest.TestCase): + def test_bundle_id(self): + self.assertEqual(bundle_id("foo", "bar"), + "force.bdss.bundles.foo.bar") + + for bad_entry in ["", None, " ", "foo bar"]: + with self.assertRaises(ValueError): + bundle_id(bad_entry, "bar") + with self.assertRaises(ValueError): + bundle_id("foo", bad_entry) diff --git a/force_bdss/workspecs/data_source.py b/force_bdss/workspecs/data_source.py index 944e41ce68b6fa60fd9be2c77a6f651df2832b00..bad5752c63bdf9bb9b3d76256592d9ec875c53c4 100644 --- a/force_bdss/workspecs/data_source.py +++ b/force_bdss/workspecs/data_source.py @@ -1,15 +1,14 @@ -from traits.has_traits import HasStrictTraits -from traits.trait_types import String, Dict +from traits.api import HasStrictTraits, String, Dict class DataSource(HasStrictTraits): - name = String() + id = String() model_data = Dict() @classmethod def from_json(cls, json_data): self = cls( - name=json_data["name"], + id=json_data["id"], model_data=json_data["model_data"] ) diff --git a/force_bdss/workspecs/kpi_calculator.py b/force_bdss/workspecs/kpi_calculator.py index ec41bbfa1b5f5e7dc083c84066e61440b2771810..b8066259538af6b8e19168d4404a432e3f2bdadc 100644 --- a/force_bdss/workspecs/kpi_calculator.py +++ b/force_bdss/workspecs/kpi_calculator.py @@ -1,15 +1,14 @@ -from traits.has_traits import HasStrictTraits -from traits.trait_types import String, Dict +from traits.api import HasStrictTraits, String, Dict class KPICalculator(HasStrictTraits): - name = String() + id = String() model_data = Dict() @classmethod def from_json(cls, json_data): self = cls( - name=json_data["name"], + id=json_data["id"], model_data=json_data["model_data"] ) diff --git a/force_bdss/workspecs/multi_criteria_optimizer.py b/force_bdss/workspecs/multi_criteria_optimizer.py index 72a2fa6b264fb3f5cd0be4fc9af9fb284a6fca96..efa4bfd9428e7c8e6a4d093649b0fc07d79c81a8 100644 --- a/force_bdss/workspecs/multi_criteria_optimizer.py +++ b/force_bdss/workspecs/multi_criteria_optimizer.py @@ -2,13 +2,13 @@ from traits.api import HasStrictTraits, String, Dict class MultiCriteriaOptimizer(HasStrictTraits): - name = String() + id = String() model_data = Dict() @classmethod def from_json(cls, json_data): self = cls( - name=json_data["name"], + id=json_data["id"], model_data=json_data["model_data"] )