From b518ef5b32e3d38220fa51895c59061fbfe0ed11 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Wed, 5 Jul 2023 17:43:14 -0700 Subject: [PATCH 01/16] Add callbacks for setting, getting, and resetting. In order to implement support for more aspects of SED-ML (and to implement support for more basic SED-ML for non-XML languages), we need to let the simulators set and get model variables directly. This implements a support structure for such systems, that falls back to the current behavior. --- biosimulators_utils/sedml/exec.py | 69 ++++++++++++++++++++---------- biosimulators_utils/sedml/utils.py | 6 +-- tests/sedml/test_sedml_utils.py | 44 ++++++++++++++++++- 3 files changed, 91 insertions(+), 28 deletions(-) diff --git a/biosimulators_utils/sedml/exec.py b/biosimulators_utils/sedml/exec.py index 949a0dbf..99aa7e4b 100644 --- a/biosimulators_utils/sedml/exec.py +++ b/biosimulators_utils/sedml/exec.py @@ -51,7 +51,8 @@ def exec_sed_doc(task_executer, doc, working_dir, base_out_path, rel_out_path=No apply_xml_model_changes=False, log=None, indent=0, pretty_print_modified_xml_models=False, log_level=StandardOutputErrorCapturerLevel.c, - config=None): + config=None, get_value_executer=None, set_value_executer=None, preprocessed_task_executer=None, + reset_executer=None): """ Execute the tasks specified in a SED document and generate the specified outputs Args: @@ -182,17 +183,25 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** model_etrees[original_model.id] = model_etree task_vars = get_variables_for_task(doc, task) + preprocessed_task = None + if preprocessed_task_executer: + preprocessed_task = preprocessed_task_executer(task, task_vars, config=config) + # execute task if isinstance(task, Task): - task_var_results = exec_task(task, task_executer, task_vars, doc, log=task_log, config=config) + task_var_results = exec_task(task, task_executer, task_vars, doc, + preprocessed_task=preprocessed_task, log=task_log, config=config) elif isinstance(task, RepeatedTask): task_var_results = exec_repeated_task(task, task_executer, task_vars, doc, apply_xml_model_changes=apply_xml_model_changes, model_etrees=model_etrees, pretty_print_modified_xml_models=pretty_print_modified_xml_models, - config=config) + config=config, preprocessed_task=preprocessed_task, + get_value_executer=get_value_executer, + set_value_executer=set_value_executer, + reset_executer=reset_executer) else: # pragma: no cover: already validated by :obj:`get_models_referenced_by_task` raise NotImplementedError('Tasks of type {} are not supported.'.format(task.__class__.__name__)) @@ -363,7 +372,7 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** return report_results, log -def exec_task(task, task_executer, task_vars, doc, log=None, config=None): +def exec_task(task, task_executer, task_vars, doc, log=None, config=None, preprocessed_task=None): """ Execute a basic SED task Args: @@ -401,7 +410,7 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** :obj:`VariableResults`: results of the variables """ # execute task - task_variable_results, _ = task_executer(task, task_vars, log=log, config=config) + task_variable_results, _ = task_executer(task, task_vars, log=log, config=config, preprocessed_task=preprocessed_task) # check that the expected variables were recorded variable_results = VariableResults() @@ -413,7 +422,8 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** def exec_repeated_task(task, task_executer, task_vars, doc, apply_xml_model_changes=False, model_etrees=None, - pretty_print_modified_xml_models=False, config=None): + pretty_print_modified_xml_models=False, config=None, preprocessed_task=None, get_value_executer=None, + set_value_executer=None, reset_executer=None): """ Execute a repeated SED task Args: @@ -451,7 +461,7 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** :obj:`VariableResults`: results of the variables """ # warn about inability to not reset models - if not task.reset_model_for_each_iteration: + if not task.reset_model_for_each_iteration and not reset_executer: models = get_first_last_models_executed_by_task(task) if models[0] == models[-1]: msg = ( @@ -462,7 +472,7 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** sub_tasks = sorted(task.sub_tasks, key=lambda sub_task: sub_task.order) for prev_sub_task, next_sub_task in zip(sub_tasks[0:-1], sub_tasks[1:]): - if get_first_last_models_executed_by_task(prev_sub_task.task)[-1] == get_first_last_models_executed_by_task(next_sub_task.task)[0]: + if get_first_last_models_executed_by_task(prev_sub_task.task)[-1] == get_first_last_models_executed_by_task(next_sub_task.task)[0] and not reset_executer: msg = ( 'Only independent execution of sub-tasks is supported. ' 'Successive sub-tasks will not be executed starting from the end state of the previous sub-task.' @@ -500,6 +510,8 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** doc = copy.deepcopy(original_doc) task = next(task for task in doc.tasks if task.id == original_task.id) model_etrees = copy.deepcopy(original_model_etrees) + if reset_executer: + reset_executer(preprocessed_task) # get range values current_range_values = {} @@ -514,27 +526,35 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** for change in task.changes: variable_values = {} for variable in change.variables: - if not apply_xml_model_changes: + if get_value_executer and preprocessed_task: + value = get_value_executer(change.model, variable, preprocessed_task) + variable_values[variable.id] = value + elif not apply_xml_model_changes: raise NotImplementedError('Set value changes that involve variables of non-XML-encoded models are not supported.') - variable_values[variable.id] = get_value_of_variable_model_xml_targets(variable, model_etrees) + else: + variable_values[variable.id] = get_value_of_variable_model_xml_targets(variable, model_etrees) new_value = calc_compute_model_change_new_value(change, variable_values=variable_values, range_values=current_range_values) - if new_value == int(new_value): - new_value = str(int(new_value)) + + if set_value_executer: + set_value_executer(change.model, change.target, change.symbol, new_value, preprocessed_task) else: - new_value = str(new_value) + if new_value == int(new_value): + new_value = str(int(new_value)) + else: + new_value = str(new_value) - if change.symbol: - raise NotImplementedError('Set value changes of symbols is not supported.') + if change.symbol: + raise NotImplementedError('Set value changes of symbols is not supported.') - attr_change = ModelAttributeChange(target=change.target, target_namespaces=change.target_namespaces, new_value=new_value) + attr_change = ModelAttributeChange(target=change.target, target_namespaces=change.target_namespaces, new_value=new_value) - if apply_xml_model_changes and is_model_language_encoded_in_xml(change.model.language): - model = Model(changes=[attr_change]) - apply_changes_to_xml_model(model, model_etrees[change.model.id], None, None) + if apply_xml_model_changes and is_model_language_encoded_in_xml(change.model.language): + model = Model(changes=[attr_change]) + apply_changes_to_xml_model(model, model_etrees[change.model.id], None, None) - else: - change.model.changes.append(attr_change) + else: + change.model.changes.append(attr_change) # sort the sub-tasks sub_tasks = sorted(task.sub_tasks, key=lambda sub_task: sub_task.order) @@ -554,7 +574,7 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** standalone=False, pretty_print=pretty_print_modified_xml_models) - sub_task_var_results = exec_task(sub_task.task, task_executer, task_vars, doc, config=config) + sub_task_var_results = exec_task(sub_task.task, task_executer, task_vars, doc, config=config, preprocessed_task=preprocessed_task) if apply_xml_model_changes and is_model_language_encoded_in_xml(model.language): os.remove(model.source) @@ -565,7 +585,10 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** apply_xml_model_changes=apply_xml_model_changes, model_etrees=model_etrees, pretty_print_modified_xml_models=pretty_print_modified_xml_models, - config=config) + config=config, preprocessed_task=preprocessed_task, + get_value_executer=get_value_executer, + set_value_executer=set_value_executer, + reset_executer=reset_executer) else: # pragma: no cover: already validated by :obj:`get_first_last_models_executed_by_task` raise NotImplementedError('Tasks of type {} are not supported.'.format(sub_task.task.__class__.__name__)) diff --git a/biosimulators_utils/sedml/utils.py b/biosimulators_utils/sedml/utils.py index ecbecdfd..fec7ce17 100644 --- a/biosimulators_utils/sedml/utils.py +++ b/biosimulators_utils/sedml/utils.py @@ -424,7 +424,7 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non # get object to change obj_xpath, sep, attr = change.target.rpartition('/@') if sep != '/@': - raise ValueError('target {} is not a valid XPath to an attribute of a model element'.format(change.target)) + raise NotImplementedError('target {} cannot be changed by XML manipulation, as the target is not an attribute of a model element'.format(change.target)) objs = eval_xpath(model_etree, obj_xpath, change.target_namespaces) if validate_unique_xml_targets and len(objs) != 1: raise ValueError('xpath {} must match a single object'.format(obj_xpath)) @@ -502,7 +502,7 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non # get object to change obj_xpath, sep, attr = change.target.rpartition('/@') if sep != '/@': - raise ValueError('target {} is not a valid XPath to an attribute of a model element'.format(change.target)) + raise NotImplementedError('target {} cannot be changed by XML manipulation, as the target is not an attribute of a model element'.format(change.target)) objs = eval_xpath(model_etree, obj_xpath, change.target_namespaces) if validate_unique_xml_targets and len(objs) != 1: raise ValueError('xpath {} must match a single object'.format(obj_xpath)) @@ -575,7 +575,7 @@ def get_value_of_variable_model_xml_targets(variable, model_etrees): obj_xpath, sep, attr = variable.target.rpartition('/@') if sep != '/@': - raise ValueError('target {} is not a valid XPath to an attribute of a model element'.format(variable.target)) + raise NotImplementedError('the value of target {} cannot be obtained by examining the XML, as the target is not an attribute of a model element'.format(variable.target)) et = model_etrees[variable.model.id] obj = eval_xpath(et, obj_xpath, variable.target_namespaces) diff --git a/tests/sedml/test_sedml_utils.py b/tests/sedml/test_sedml_utils.py index bb2e8eb7..aa93837f 100644 --- a/tests/sedml/test_sedml_utils.py +++ b/tests/sedml/test_sedml_utils.py @@ -768,7 +768,7 @@ def test_apply_compute_model_change_new_value(self): } change.variables[0].target = "/model/parameter[@id='x']" - with self.assertRaisesRegex(ValueError, 'not a valid XPath'): + with self.assertRaisesRegex(NotImplementedError, 'cannot be obtained by examining the XML'): utils.get_value_of_variable_model_xml_targets(change.variables[0], models) change.variables[0].target = "/model/parameter/@value" @@ -864,7 +864,7 @@ def test_apply_compute_model_change_new_value(self): change.target = "/model/parameter[@type='parameter']" et = etree.parse(in_file) - with self.assertRaisesRegex(ValueError, 'not a valid XPath to an attribute'): + with self.assertRaisesRegex(NotImplementedError, 'cannot be changed by XML manipulation'): utils.apply_changes_to_xml_model(data_model.Model(changes=[change]), et, None, None, variable_values=variable_values) with open(in_file, 'w') as file: @@ -880,6 +880,43 @@ def test_apply_compute_model_change_new_value(self): change.target_namespaces['qual'] = "https://qual.sbml.org" utils.apply_changes_to_xml_model(data_model.Model(changes=[change]), et, None, None, variable_values=variable_values) + def test_apply_compute_model_change_new_value_only_objects(self): + change = data_model.ComputeModelChange( + target="/model/parameter[@id='p1']", + parameters=[ + data_model.Parameter(id='a', value=1.5), + data_model.Parameter(id='b', value=2.25), + data_model.Parameter(id='c', value=2.), + ], + variables=[ + data_model.Variable(id='x', model=data_model.Model(id='model_1'), target="/model/parameter[@id='x']"), + data_model.Variable(id='y', model=data_model.Model(id='model_2'), target="/model/parameter[@id='y']"), + ], + math='a * x + b * y', + ) + + # get values of variables + model_filename = os.path.join(self.tmp_dir, 'model_1.xml') + with open(model_filename, 'w') as file: + file.write('') + file.write('') + file.write('') + file.write('') + models = { + 'model_1': etree.parse(model_filename), + 'model_2': etree.parse(model_filename), + } + + change.variables[0].target = None + change.variables[0].symbol = True + + change.variables[0].target = "/model/parameter[@id='x']" + change.variables[0].symbol = None + with self.assertRaisesRegex(NotImplementedError, 'cannot be obtained by examining the XML'): + self.assertEqual(utils.get_value_of_variable_model_xml_targets(change.variables[0], models), 2.0) + with self.assertRaisesRegex(NotImplementedError, 'cannot be obtained by examining the XML'): + self.assertEqual(utils.get_value_of_variable_model_xml_targets(change.variables[1], models), 3.0) + def test_set_value_calc_compute_model_change_new_value(self): change = data_model.SetValueComputeModelChange( target="/model/parameter[@id='p1']/@value", @@ -1647,3 +1684,6 @@ def test_get_task_results_shape(self): ], ) self.assertEqual(utils.get_task_results_shape(task), (5, 1, 3, 2, 11)) + +if __name__ == "__main__": + unittest.main() From c9a67c42863478dd0aa62568c5fac2264d20bee8 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Wed, 5 Jul 2023 17:56:40 -0700 Subject: [PATCH 02/16] Fix linting. --- biosimulators_utils/sedml/exec.py | 6 +++--- biosimulators_utils/sedml/utils.py | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/biosimulators_utils/sedml/exec.py b/biosimulators_utils/sedml/exec.py index 99aa7e4b..bd977df5 100644 --- a/biosimulators_utils/sedml/exec.py +++ b/biosimulators_utils/sedml/exec.py @@ -187,10 +187,9 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** if preprocessed_task_executer: preprocessed_task = preprocessed_task_executer(task, task_vars, config=config) - # execute task if isinstance(task, Task): - task_var_results = exec_task(task, task_executer, task_vars, doc, + task_var_results = exec_task(task, task_executer, task_vars, doc, preprocessed_task=preprocessed_task, log=task_log, config=config) elif isinstance(task, RepeatedTask): @@ -472,7 +471,8 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** sub_tasks = sorted(task.sub_tasks, key=lambda sub_task: sub_task.order) for prev_sub_task, next_sub_task in zip(sub_tasks[0:-1], sub_tasks[1:]): - if get_first_last_models_executed_by_task(prev_sub_task.task)[-1] == get_first_last_models_executed_by_task(next_sub_task.task)[0] and not reset_executer: + if get_first_last_models_executed_by_task(prev_sub_task.task)[-1] == get_first_last_models_executed_by_task(next_sub_task.task)[0] \ + and not reset_executer: msg = ( 'Only independent execution of sub-tasks is supported. ' 'Successive sub-tasks will not be executed starting from the end state of the previous sub-task.' diff --git a/biosimulators_utils/sedml/utils.py b/biosimulators_utils/sedml/utils.py index fec7ce17..3b87f50e 100644 --- a/biosimulators_utils/sedml/utils.py +++ b/biosimulators_utils/sedml/utils.py @@ -424,7 +424,8 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non # get object to change obj_xpath, sep, attr = change.target.rpartition('/@') if sep != '/@': - raise NotImplementedError('target {} cannot be changed by XML manipulation, as the target is not an attribute of a model element'.format(change.target)) + raise NotImplementedError('target ' + change.target + ' cannot be changed by XML manipulation, as the target ' + 'is not an attribute of a model element') objs = eval_xpath(model_etree, obj_xpath, change.target_namespaces) if validate_unique_xml_targets and len(objs) != 1: raise ValueError('xpath {} must match a single object'.format(obj_xpath)) @@ -502,7 +503,8 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non # get object to change obj_xpath, sep, attr = change.target.rpartition('/@') if sep != '/@': - raise NotImplementedError('target {} cannot be changed by XML manipulation, as the target is not an attribute of a model element'.format(change.target)) + raise NotImplementedError('target ' + change.target +' cannot be changed by XML manipulation, as the target ' + 'is not an attribute of a model element') objs = eval_xpath(model_etree, obj_xpath, change.target_namespaces) if validate_unique_xml_targets and len(objs) != 1: raise ValueError('xpath {} must match a single object'.format(obj_xpath)) @@ -575,7 +577,8 @@ def get_value_of_variable_model_xml_targets(variable, model_etrees): obj_xpath, sep, attr = variable.target.rpartition('/@') if sep != '/@': - raise NotImplementedError('the value of target {} cannot be obtained by examining the XML, as the target is not an attribute of a model element'.format(variable.target)) + raise NotImplementedError('the value of target ' + variable.target + + ' cannot be obtained by examining the XML, as the target is not an attribute of a model element') et = model_etrees[variable.model.id] obj = eval_xpath(et, obj_xpath, variable.target_namespaces) From 2167185ec080833d7ca61d18e98d10a2960e1e02 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Wed, 5 Jul 2023 18:03:21 -0700 Subject: [PATCH 03/16] More linting. --- biosimulators_utils/sedml/exec.py | 2 +- biosimulators_utils/sedml/utils.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/biosimulators_utils/sedml/exec.py b/biosimulators_utils/sedml/exec.py index bd977df5..ab2887a3 100644 --- a/biosimulators_utils/sedml/exec.py +++ b/biosimulators_utils/sedml/exec.py @@ -535,7 +535,7 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** variable_values[variable.id] = get_value_of_variable_model_xml_targets(variable, model_etrees) new_value = calc_compute_model_change_new_value(change, variable_values=variable_values, range_values=current_range_values) - + if set_value_executer: set_value_executer(change.model, change.target, change.symbol, new_value, preprocessed_task) else: diff --git a/biosimulators_utils/sedml/utils.py b/biosimulators_utils/sedml/utils.py index 3b87f50e..3af9db0e 100644 --- a/biosimulators_utils/sedml/utils.py +++ b/biosimulators_utils/sedml/utils.py @@ -503,7 +503,7 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non # get object to change obj_xpath, sep, attr = change.target.rpartition('/@') if sep != '/@': - raise NotImplementedError('target ' + change.target +' cannot be changed by XML manipulation, as the target ' + raise NotImplementedError('target ' + change.target + ' cannot be changed by XML manipulation, as the target ' 'is not an attribute of a model element') objs = eval_xpath(model_etree, obj_xpath, change.target_namespaces) if validate_unique_xml_targets and len(objs) != 1: @@ -577,7 +577,7 @@ def get_value_of_variable_model_xml_targets(variable, model_etrees): obj_xpath, sep, attr = variable.target.rpartition('/@') if sep != '/@': - raise NotImplementedError('the value of target ' + variable.target + + raise NotImplementedError('the value of target ' + variable.target + ' cannot be obtained by examining the XML, as the target is not an attribute of a model element') et = model_etrees[variable.model.id] From c201f48c5533094f88b0cd843ec108e0d18abea9 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Wed, 5 Jul 2023 18:35:40 -0700 Subject: [PATCH 04/16] Try to fix mocked functions. --- tests/sedml/test_sedml_exec.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/sedml/test_sedml_exec.py b/tests/sedml/test_sedml_exec.py index 688b7e95..13e194af 100644 --- a/tests/sedml/test_sedml_exec.py +++ b/tests/sedml/test_sedml_exec.py @@ -186,7 +186,7 @@ def test_successful(self): filename = os.path.join(self.tmp_dir, 'test.sedml') io.SedmlSimulationWriter().run(doc, filename, validate_models_with_languages=False) - def exec_task(task, variables, log=None, config=None): + def exec_task(task, variables, log=None, config=None, preprocessed_task=None): results = VariableResults() if task.id == 'task_1_ss': results[doc.data_generators[0].variables[0].id] = numpy.array((1., 2.)) @@ -555,7 +555,7 @@ def test_with_model_changes(self): os.path.join(os.path.dirname(__file__), '..', 'fixtures', 'sbml-three-species.xml'), os.path.join(working_dir, 'model1.xml')) - def exec_task(task, variables, log=None, config=None): + def exec_task(task, variables, log=None, config=None, preprocessed_task=None): et = etree.parse(task.model.source) results = VariableResults() @@ -591,7 +591,7 @@ def test_warnings(self): filename = os.path.join(self.tmp_dir, 'test.sedml') io.SedmlSimulationWriter().run(doc, filename) - def exec_task(task, variables, log=None, config=None): + def exec_task(task, variables, log=None, config=None, preprocessed_task=None): return VariableResults(), log out_dir = os.path.join(self.tmp_dir, 'results') @@ -651,7 +651,7 @@ def exec_task(task, variables, log=None, config=None): filename = os.path.join(self.tmp_dir, 'test.sedml') io.SedmlSimulationWriter().run(doc, filename, validate_models_with_languages=False) - def exec_task(task, variables, log=None, config=None): + def exec_task(task, variables, log=None, config=None, preprocessed_task=None): if task.id == 'task1': return VariableResults({'data_gen_1_var_1': numpy.array(1.)}), log else: @@ -720,7 +720,7 @@ def test_errors(self): filename = os.path.join(self.tmp_dir, 'test.sedml') io.SedmlSimulationWriter().run(doc, filename, validate_models_with_languages=False) - def exec_task(task, variables, log=None, config=None): + def exec_task(task, variables, log=None, config=None, preprocessed_task=None): return VariableResults(), log working_dir = os.path.dirname(filename) @@ -786,7 +786,7 @@ def exec_task(task, variables, log=None, config=None): ], )) - def exec_task(task, variables, log=None, config=None): + def exec_task(task, variables, log=None, config=None, preprocessed_task=None): results = VariableResults() results[doc.data_generators[0].variables[0].id] = numpy.array((1.,)) results[doc.data_generators[0].variables[1].id] = numpy.array((1.,)) @@ -827,7 +827,7 @@ def exec_task(task, variables, log=None, config=None): ) ] - def exec_task(task, variables, log=None, config=None): + def exec_task(task, variables, log=None, config=None, preprocessed_task=None): results = VariableResults() results[doc.data_generators[0].variables[0].id] = numpy.array((1.,)) return results, log @@ -875,7 +875,7 @@ def exec_task(task, variables, log=None, config=None): ), ] - def exec_task(task, variables, log=None, config=None): + def exec_task(task, variables, log=None, config=None, preprocessed_task=None): results = VariableResults() results[doc.data_generators[0].variables[0].id] = numpy.array((1.,)) results[doc.data_generators[0].variables[1].id] = numpy.array((1., 2.)) @@ -947,7 +947,7 @@ def exec_task(task, variables, log=None, config=None): ), ] - def exec_task(task, variables, log=None, config=None): + def exec_task(task, variables, log=None, config=None, preprocessed_task=None): results = VariableResults() results[doc.data_generators[0].variables[0].id] = numpy.array((1.,)) results[doc.data_generators[1].variables[0].id] = numpy.array((1., 2.)) @@ -1045,7 +1045,7 @@ def exec_task(task, variables, log=None, config=None): ), ] - def exec_task(task, variables, log=None, config=None): + def exec_task(task, variables, log=None, config=None, preprocessed_task=None): results = VariableResults() results[doc.data_generators[0].variables[0].id] = numpy.array((1., 2.)) results[doc.data_generators[1].variables[0].id] = numpy.array((2., 3.)) @@ -1171,7 +1171,7 @@ def test_2d_plot(self): filename = os.path.join(self.tmp_dir, 'test.sedml') io.SedmlSimulationWriter().run(doc, filename, validate_models_with_languages=False) - def exec_task(task, variables, log=None, config=None): + def exec_task(task, variables, log=None, config=None, preprocessed_task=None): results = VariableResults() results[doc.data_generators[0].variables[0].id] = numpy.linspace(0., 10., 10 + 1) results[doc.data_generators[1].variables[0].id] = 2 * results[doc.data_generators[0].variables[0].id] @@ -1274,7 +1274,7 @@ def exec_task(task, variables, log=None, config=None): ) # error with a task - def exec_task(task, variables, log=None, config=None): + def exec_task(task, variables, log=None, config=None, preprocessed_task=None): results = VariableResults() results[doc.data_generators[0].variables[0].id] = None results[doc.data_generators[1].variables[0].id] = 2 * numpy.linspace(0., 10., 10 + 1) @@ -1421,7 +1421,7 @@ def test_3d_plot(self): filename = os.path.join(self.tmp_dir, 'test.sedml') io.SedmlSimulationWriter().run(doc, filename, validate_models_with_languages=False) - def exec_task(task, variables, log=None, config=None): + def exec_task(task, variables, log=None, config=None, preprocessed_task=None): results = VariableResults() x = numpy.arange(-5, 5, 0.25) x, _ = numpy.meshgrid(x, x) @@ -1906,7 +1906,7 @@ def test_exec_without_log(self): filename = os.path.join(self.tmp_dir, 'test.sedml') io.SedmlSimulationWriter().run(doc, filename, validate_models_with_languages=False) - def exec_task(task, variables, log=None, config=None): + def exec_task(task, variables, log=None, config=None, preprocessed_task=None): results = VariableResults() results[doc.data_generators[0].variables[0].id] = numpy.array((1., 2.)) return results, log From 4ec7a2b12630bea006140ce91acf253e5b321205 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Wed, 5 Jul 2023 19:12:12 -0700 Subject: [PATCH 05/16] Try to fix mocked functions. --- tests/combine/test_combine_exec.py | 2 +- tests/sedml/test_sedml_exec.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/combine/test_combine_exec.py b/tests/combine/test_combine_exec.py index 4b25d2da..c26d3522 100644 --- a/tests/combine/test_combine_exec.py +++ b/tests/combine/test_combine_exec.py @@ -383,7 +383,7 @@ def test_exec_sedml_docs_in_archive_without_log(self): archive_filename = os.path.join(self.tmp_dir, 'archive.omex') CombineArchiveWriter().run(archive, archive_dirname, archive_filename) - def sed_task_executer(task, variables, log=None, config=None): + def sed_task_executer(task, variables, log=None, config=None, preprocessed_task=None): if log: log.algorithm = task.simulation.algorithm.kisao_id log.simulator_details = { diff --git a/tests/sedml/test_sedml_exec.py b/tests/sedml/test_sedml_exec.py index 13e194af..e831795a 100644 --- a/tests/sedml/test_sedml_exec.py +++ b/tests/sedml/test_sedml_exec.py @@ -1662,7 +1662,7 @@ def test_exec_repeated_task(self): model2.id: etree.parse(model_filename2), } - def task_executer(task, variables, log=None, config=None): + def task_executer(task, variables, log=None, config=None, preprocessed_task=None): et = etree.parse(task.model.source) if task.id == task1.id: From 2bae81f32411a642f588404dc2379f8b49af4a14 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Wed, 5 Jul 2023 19:35:13 -0700 Subject: [PATCH 06/16] More fixing mocked functions. --- tests/combine/test_combine_exec.py | 4 ++-- tests/sedml/test_sedml_exec.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/combine/test_combine_exec.py b/tests/combine/test_combine_exec.py index c26d3522..8dee88a7 100644 --- a/tests/combine/test_combine_exec.py +++ b/tests/combine/test_combine_exec.py @@ -395,7 +395,7 @@ def sed_task_executer(task, variables, log=None, config=None, preprocessed_task= 'var_2': numpy.linspace(10., 20., task.simulation.number_of_points + 1), }), log - def sed_task_executer_error(task, variables, log=None, config=None): + def sed_task_executer_error(task, variables, log=None, config=None, preprocessed_task=None): raise ValueError('Big error') out_dir = os.path.join(self.tmp_dir, 'outputs') @@ -464,7 +464,7 @@ def exec_sed_doc(task_executer, filename, working_dir, base_out_dir, indent=0, log=None, log_level=None, config=None): return None, None - def sed_task_executer(task, variables): + def sed_task_executer(task, variables, preprocessed_task=None): pass sed_doc_executer = functools.partial(exec_sed_doc, sed_task_executer) diff --git a/tests/sedml/test_sedml_exec.py b/tests/sedml/test_sedml_exec.py index e831795a..8613a36a 100644 --- a/tests/sedml/test_sedml_exec.py +++ b/tests/sedml/test_sedml_exec.py @@ -1781,7 +1781,7 @@ def test_exec_sed_doc_with_repeated_task(self): file.write(' ') file.write('') - def task_executer(task, variables, log=None, config=None): + def task_executer(task, variables, log=None, config=None, preprocessed_task=None): results = VariableResults({ 'x': numpy.linspace(10., 15., 6), 'y': numpy.linspace(20., 25., 6), @@ -1829,7 +1829,7 @@ def test_capturer_not_available(self): file.write(' ') file.write('') - def task_executer(task, variables, log=None, config=None): + def task_executer(task, variables, log=None, config=None, preprocessed_task=None): results = VariableResults({ 'x': numpy.linspace(10., 15., 6), }) From a7dc243eb01ec85a3febb1a98baacc849023f3af Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Wed, 5 Jul 2023 20:01:04 -0700 Subject: [PATCH 07/16] New error thrown. --- tests/sedml/test_sedml_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/sedml/test_sedml_utils.py b/tests/sedml/test_sedml_utils.py index aa93837f..5e085507 100644 --- a/tests/sedml/test_sedml_utils.py +++ b/tests/sedml/test_sedml_utils.py @@ -685,7 +685,7 @@ def test_errors(self): target_namespaces={'sbml': 'http://www.sbml.org/sbml/level2/version4'}, new_value='1.9') et = etree.parse(self.FIXTURE_FILENAME) - with self.assertRaises(ValueError): + with self.assertRaises(NotImplementedError): utils.apply_changes_to_xml_model(data_model.Model(changes=[change]), et, None, None) change = data_model.ModelAttributeChange( From f7508b41d0461ca18a836dbff5b52c821a99e0ad Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Fri, 14 Jul 2023 14:32:08 -0700 Subject: [PATCH 08/16] Variables do indeed need to reference models sometimes. --- biosimulators_utils/sedml/validation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/biosimulators_utils/sedml/validation.py b/biosimulators_utils/sedml/validation.py index 29f4522f..b29953fc 100644 --- a/biosimulators_utils/sedml/validation.py +++ b/biosimulators_utils/sedml/validation.py @@ -1386,8 +1386,8 @@ def validate_data_generator_variables(variables, model_etrees=None, validate_tar if not variable.id: variable_errors.append(['Variable must have an id.']) - if variable.model: - variable_errors.append(['Variable should not reference a model.']) + # if variable.model: + # variable_errors.append(['Variable should not reference a model.']) if variable.task: task_types.add(get_task_results_shape(variable.task)) From 08a4d521b33bb21584bdad31786d294907f7e54d Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Fri, 14 Jul 2023 15:59:48 -0700 Subject: [PATCH 09/16] Don't check for error we removed. --- tests/sedml/test_sedml_validation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/sedml/test_sedml_validation.py b/tests/sedml/test_sedml_validation.py index 2da94095..f4778561 100644 --- a/tests/sedml/test_sedml_validation.py +++ b/tests/sedml/test_sedml_validation.py @@ -234,7 +234,7 @@ def test_validate_doc(self): ], )) errors, warnings = validation.validate_doc(doc, self.dirname) - self.assertIn('should not reference a model', flatten_nested_list_of_strings(errors)) + # self.assertIn('should not reference a model', flatten_nested_list_of_strings(errors)) self.assertEqual(warnings, []) doc = data_model.SedDocument() @@ -1210,7 +1210,7 @@ def test_validate_task(self): data_model.Variable(task=data_model.Task(), model=data_model.Model()) ] errors, warnings = self._validate_task(task, variables) - self.assertIn('should not reference a model', flatten_nested_list_of_strings(errors)) + # self.assertIn('should not reference a model', flatten_nested_list_of_strings(errors)) self.assertEqual(warnings, []) variables = [ From 2c5da53c2107d861459bcd98c18171455626ebea Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Tue, 18 Jul 2023 15:31:13 -0700 Subject: [PATCH 10/16] Changes to allow model changes to also be set on model load. The most awkward part of this is that we needed to allow XML-based changes to happen first (add/remove/change), then write that out, then create the interpreter's version of the model by reading *that* in, then making further changes from changeAttribute and computeChange. --- biosimulators_utils/sedml/exec.py | 22 +++-- biosimulators_utils/sedml/utils.py | 139 +++++++++++++++++++---------- 2 files changed, 106 insertions(+), 55 deletions(-) diff --git a/biosimulators_utils/sedml/exec.py b/biosimulators_utils/sedml/exec.py index ab2887a3..84259fd9 100644 --- a/biosimulators_utils/sedml/exec.py +++ b/biosimulators_utils/sedml/exec.py @@ -27,6 +27,7 @@ from lxml import etree # noqa: F401 import copy import datetime +import functools import numpy import os import sys @@ -165,14 +166,24 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** original_model_changes = {} temp_model_sources = [] model_etrees = {} + preprocessed_task = None + + task_vars = get_variables_for_task(doc, task) + preprocessed_task_sub_executer = None + if preprocessed_task_executer: + preprocessed_task_sub_executer = functools.partial(preprocessed_task_executer, + task, task_vars, + config=config) + for original_model in original_models: original_model_sources[original_model.id] = original_model.source original_model_changes[original_model.id] = original_model.changes - temp_model, temp_model_source, model_etree = resolve_model_and_apply_xml_changes( + temp_model, temp_model_source, model_etree, preprocessed_task = resolve_model_and_apply_xml_changes( original_model, doc, working_dir, apply_xml_model_changes=apply_xml_model_changes, - pretty_print_modified_xml_models=pretty_print_modified_xml_models) + pretty_print_modified_xml_models=pretty_print_modified_xml_models, + set_value_executer=set_value_executer, preprocessed_task_sub_executer=preprocessed_task_sub_executer) original_model.source = temp_model.source original_model.changes = temp_model.changes @@ -182,10 +193,9 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** model_etrees[original_model.id] = model_etree - task_vars = get_variables_for_task(doc, task) - preprocessed_task = None - if preprocessed_task_executer: - preprocessed_task = preprocessed_task_executer(task, task_vars, config=config) + # The preprocessed task was not created if there was no set_value_executer, so create one now: + if not preprocessed_task and preprocessed_task_executer: + preprocessed_task_sub_executer() # execute task if isinstance(task, Task): diff --git a/biosimulators_utils/sedml/utils.py b/biosimulators_utils/sedml/utils.py index 3af9db0e..9db117b4 100644 --- a/biosimulators_utils/sedml/utils.py +++ b/biosimulators_utils/sedml/utils.py @@ -258,10 +258,12 @@ def get_model_changes_for_task(task): BIOMODELS_DOWNLOAD_ENDPOINT = 'https://www.ebi.ac.uk/biomodels/model/download/{}?filename={}_url.xml' -def resolve_model_and_apply_xml_changes(model, sed_doc, working_dir, +def resolve_model_and_apply_xml_changes(orig_model, sed_doc, working_dir, apply_xml_model_changes=True, save_to_file=True, - pretty_print_modified_xml_models=False): + pretty_print_modified_xml_models=False, + set_value_executer=None, + preprocessed_task_sub_executer=None): """ Resolve the source of a model and, optionally, apply XML changes to the model. Args: @@ -288,7 +290,9 @@ def resolve_model_and_apply_xml_changes(model, sed_doc, working_dir, a remote source of modified * :obj:`etree._Element`: element tree for the resolved/modified model """ - model = copy.deepcopy(model) + model = copy.deepcopy(orig_model) + # We need to save this because we're going to change it, then change it back. + orig_model_source = orig_model.source # resolve model temp_model_source = resolve_model(model, sed_doc, working_dir) @@ -302,8 +306,12 @@ def resolve_model_and_apply_xml_changes(model, sed_doc, working_dir, raise ValueError('The model could not be parsed because the model is not a valid XML document: {}'.format(str(exception))) if model.changes: + # Change source here so that tasks point to actual source they can find. + orig_model.source = model.source # apply changes - apply_changes_to_xml_model(model, model_etree, sed_doc, working_dir) + preprocessed_task = apply_changes_to_xml_model(model, model_etree, sed_doc, working_dir, + set_value_executer=set_value_executer, + preprocessed_task_sub_executer=preprocessed_task_sub_executer) model.changes.clear() # write model to file @@ -321,7 +329,9 @@ def resolve_model_and_apply_xml_changes(model, sed_doc, working_dir, else: model_etree = None - return model, temp_model_source, model_etree + # Reset the model source, in case it matters. + orig_model.source = orig_model_source + return model, temp_model_source, model_etree, preprocessed_task def resolve_model(model, sed_doc, working_dir): @@ -401,7 +411,8 @@ def resolve_model(model, sed_doc, working_dir): def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=None, variable_values=None, range_values=None, - validate_unique_xml_targets=True): + validate_unique_xml_targets=True, + set_value_executer=None, preprocessed_task_sub_executer=None): """ Modify an XML-encoded model according to a model change Args: @@ -418,30 +429,10 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non validate_unique_xml_targets (:obj:`bool`, optional): whether to validate the XML targets match uniue objects """ + + # First pass: Must-be-XML changes: for change in model.changes: - if isinstance(change, ModelAttributeChange): - - # get object to change - obj_xpath, sep, attr = change.target.rpartition('/@') - if sep != '/@': - raise NotImplementedError('target ' + change.target + ' cannot be changed by XML manipulation, as the target ' - 'is not an attribute of a model element') - objs = eval_xpath(model_etree, obj_xpath, change.target_namespaces) - if validate_unique_xml_targets and len(objs) != 1: - raise ValueError('xpath {} must match a single object'.format(obj_xpath)) - - ns_prefix, _, attr = attr.rpartition(':') - if ns_prefix: - ns = change.target_namespaces.get(ns_prefix, None) - if ns is None: - raise ValueError('No namespace is defined with prefix `{}`'.format(ns_prefix)) - attr = '{{{}}}{}'.format(ns, attr) - - # change value - for obj in objs: - obj.set(attr, change.new_value) - - elif isinstance(change, AddElementModelChange): + if isinstance(change, AddElementModelChange): parents = eval_xpath(model_etree, change.target, change.target_namespaces) if validate_unique_xml_targets and len(parents) != 1: @@ -485,6 +476,52 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non parent = element.getparent() parent.remove(element) + elif isinstance(change, ModelAttributeChange) or isinstance(change, ComputeModelChange): + change.model = model + pass #for now + + else: + raise NotImplementedError('Change{} of type {} is not supported.'.format( + ' ' + change.name if change.name else '', change.__class__.__name__)) + + # Interlude: set up the preprocessed task, if there's a set_value_executor + preprocessed_task = None + if set_value_executer: + model_etree.write(model.source, + xml_declaration=True, + encoding="utf-8", + standalone=False, + pretty_print=True) + + if preprocessed_task_sub_executer: + preprocessed_task = preprocessed_task_sub_executer() + + # Second pass: changes that might need to be interpreter-based: + for change in model.changes: + if isinstance(change, ModelAttributeChange): + if set_value_executer: + set_value_executer(change.model, change.target, None, change.new_value, preprocessed_task) + else: + # get object to change + obj_xpath, sep, attr = change.target.rpartition('/@') + if sep != '/@': + raise NotImplementedError('target ' + change.target + ' cannot be changed by XML manipulation, as the target ' + 'is not an attribute of a model element') + objs = eval_xpath(model_etree, obj_xpath, change.target_namespaces) + if validate_unique_xml_targets and len(objs) != 1: + raise ValueError('xpath {} must match a single object'.format(obj_xpath)) + + ns_prefix, _, attr = attr.rpartition(':') + if ns_prefix: + ns = change.target_namespaces.get(ns_prefix, None) + if ns is None: + raise ValueError('No namespace is defined with prefix `{}`'.format(ns_prefix)) + attr = '{{{}}}{}'.format(ns, attr) + + # change value + for obj in objs: + obj.set(attr, change.new_value) + elif isinstance(change, ComputeModelChange): # get the values of model variables referenced by compute model changes if variable_values is None: @@ -500,29 +537,33 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non else: new_value = str(new_value) - # get object to change - obj_xpath, sep, attr = change.target.rpartition('/@') - if sep != '/@': - raise NotImplementedError('target ' + change.target + ' cannot be changed by XML manipulation, as the target ' - 'is not an attribute of a model element') - objs = eval_xpath(model_etree, obj_xpath, change.target_namespaces) - if validate_unique_xml_targets and len(objs) != 1: - raise ValueError('xpath {} must match a single object'.format(obj_xpath)) - - ns_prefix, _, attr = attr.rpartition(':') - if ns_prefix: - ns = change.target_namespaces.get(ns_prefix, None) - if ns is None: - raise ValueError('No namespace is defined with prefix `{}`'.format(ns_prefix)) - attr = '{{{}}}{}'.format(ns, attr) - - # change value - for obj in objs: - obj.set(attr, new_value) + if set_value_executer: + set_value_executer(change.model, change.target, change.symbol, new_value, preprocessed_task) + else: + # get object to change + obj_xpath, sep, attr = change.target.rpartition('/@') + if sep != '/@': + raise NotImplementedError('target ' + change.target + ' cannot be changed by XML manipulation, as the target ' + 'is not an attribute of a model element') + objs = eval_xpath(model_etree, obj_xpath, change.target_namespaces) + if validate_unique_xml_targets and len(objs) != 1: + raise ValueError('xpath {} must match a single object'.format(obj_xpath)) + + ns_prefix, _, attr = attr.rpartition(':') + if ns_prefix: + ns = change.target_namespaces.get(ns_prefix, None) + if ns is None: + raise ValueError('No namespace is defined with prefix `{}`'.format(ns_prefix)) + attr = '{{{}}}{}'.format(ns, attr) + + # change value + for obj in objs: + obj.set(attr, new_value) else: raise NotImplementedError('Change{} of type {} is not supported.'.format( ' ' + change.name if change.name else '', change.__class__.__name__)) + return preprocessed_task def get_values_of_variable_model_xml_targets_of_model_change(change, sed_doc, model_etrees, working_dir): @@ -543,7 +584,7 @@ def get_values_of_variable_model_xml_targets_of_model_change(change, sed_doc, mo for variable in change.variables: variable_model = variable.model if variable_model.id not in model_etrees: - copy_variable_model, temp_model_source, variable_model_etree = resolve_model_and_apply_xml_changes( + copy_variable_model, temp_model_source, variable_model_etree, preprocessed_task = resolve_model_and_apply_xml_changes( variable_model, sed_doc, working_dir, apply_xml_model_changes=True, save_to_file=False) From 32a0f7ecf216c6cc861dcaca9fb969500e6a4829 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Tue, 18 Jul 2023 15:38:31 -0700 Subject: [PATCH 11/16] Fix linting. --- biosimulators_utils/sedml/exec.py | 4 ++-- biosimulators_utils/sedml/utils.py | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/biosimulators_utils/sedml/exec.py b/biosimulators_utils/sedml/exec.py index 84259fd9..69b2ed94 100644 --- a/biosimulators_utils/sedml/exec.py +++ b/biosimulators_utils/sedml/exec.py @@ -171,8 +171,8 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** task_vars = get_variables_for_task(doc, task) preprocessed_task_sub_executer = None if preprocessed_task_executer: - preprocessed_task_sub_executer = functools.partial(preprocessed_task_executer, - task, task_vars, + preprocessed_task_sub_executer = functools.partial(preprocessed_task_executer, + task, task_vars, config=config) for original_model in original_models: diff --git a/biosimulators_utils/sedml/utils.py b/biosimulators_utils/sedml/utils.py index 9db117b4..a2cfb8d5 100644 --- a/biosimulators_utils/sedml/utils.py +++ b/biosimulators_utils/sedml/utils.py @@ -262,7 +262,7 @@ def resolve_model_and_apply_xml_changes(orig_model, sed_doc, working_dir, apply_xml_model_changes=True, save_to_file=True, pretty_print_modified_xml_models=False, - set_value_executer=None, + set_value_executer=None, preprocessed_task_sub_executer=None): """ Resolve the source of a model and, optionally, apply XML changes to the model. @@ -429,7 +429,7 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non validate_unique_xml_targets (:obj:`bool`, optional): whether to validate the XML targets match uniue objects """ - + # First pass: Must-be-XML changes: for change in model.changes: if isinstance(change, AddElementModelChange): @@ -478,7 +478,6 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non elif isinstance(change, ModelAttributeChange) or isinstance(change, ComputeModelChange): change.model = model - pass #for now else: raise NotImplementedError('Change{} of type {} is not supported.'.format( @@ -510,14 +509,14 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non objs = eval_xpath(model_etree, obj_xpath, change.target_namespaces) if validate_unique_xml_targets and len(objs) != 1: raise ValueError('xpath {} must match a single object'.format(obj_xpath)) - + ns_prefix, _, attr = attr.rpartition(':') if ns_prefix: ns = change.target_namespaces.get(ns_prefix, None) if ns is None: raise ValueError('No namespace is defined with prefix `{}`'.format(ns_prefix)) attr = '{{{}}}{}'.format(ns, attr) - + # change value for obj in objs: obj.set(attr, change.new_value) From ca7dda42af5f45909909a7fccfefb81eaef173ec Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Tue, 18 Jul 2023 15:50:29 -0700 Subject: [PATCH 12/16] Define preprocessed_task as None. --- biosimulators_utils/sedml/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/biosimulators_utils/sedml/utils.py b/biosimulators_utils/sedml/utils.py index a2cfb8d5..18fed5ff 100644 --- a/biosimulators_utils/sedml/utils.py +++ b/biosimulators_utils/sedml/utils.py @@ -296,6 +296,7 @@ def resolve_model_and_apply_xml_changes(orig_model, sed_doc, working_dir, # resolve model temp_model_source = resolve_model(model, sed_doc, working_dir) + preprocessed_task = None # apply changes to model if apply_xml_model_changes and model.language and is_model_language_encoded_in_xml(model.language): From 6a9c39f2f2e9dce34a95316030f24fdc07ee129d Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Tue, 18 Jul 2023 16:02:17 -0700 Subject: [PATCH 13/16] Only check unknown types the first time through the loop. --- biosimulators_utils/sedml/utils.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/biosimulators_utils/sedml/utils.py b/biosimulators_utils/sedml/utils.py index 18fed5ff..7b884bed 100644 --- a/biosimulators_utils/sedml/utils.py +++ b/biosimulators_utils/sedml/utils.py @@ -560,9 +560,6 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non for obj in objs: obj.set(attr, new_value) - else: - raise NotImplementedError('Change{} of type {} is not supported.'.format( - ' ' + change.name if change.name else '', change.__class__.__name__)) return preprocessed_task From 6b0c8c7c685bbc9e61ff2725879626fc80595272 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Tue, 18 Jul 2023 16:30:50 -0700 Subject: [PATCH 14/16] Only check unknown types the first time through the loop. --- tests/sedml/test_sedml_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/sedml/test_sedml_utils.py b/tests/sedml/test_sedml_utils.py index 5e085507..10390b36 100644 --- a/tests/sedml/test_sedml_utils.py +++ b/tests/sedml/test_sedml_utils.py @@ -1558,7 +1558,7 @@ def test_resolve_model_and_apply_xml_changes_error_handling(self): file.write('') file.write('') - temp_model, temp_model_source, temp_model_etree = utils.resolve_model_and_apply_xml_changes( + temp_model, temp_model_source, temp_model_etree, __ = utils.resolve_model_and_apply_xml_changes( model, sed_doc, self.tmp_dir, save_to_file=False) self.assertEqual(temp_model.source, model_filename) @@ -1577,7 +1577,7 @@ def test_resolve_model_and_apply_xml_changes_error_handling(self): model.language = data_model.ModelLanguage.BNGL.value with open(model_filename, 'w') as file: file.write('sbml') - temp_model, temp_model_source, temp_model_etree = utils.resolve_model_and_apply_xml_changes( + temp_model, temp_model_source, temp_model_etree, __ = utils.resolve_model_and_apply_xml_changes( model, sed_doc, self.tmp_dir, save_to_file=False) self.assertEqual(temp_model.source, model_filename) From 54342ceed576f412b820618a24721467875065f5 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Wed, 19 Jul 2023 09:17:08 -0700 Subject: [PATCH 15/16] Actually assign the created preprocessed_task to the variable. --- biosimulators_utils/sedml/exec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biosimulators_utils/sedml/exec.py b/biosimulators_utils/sedml/exec.py index 69b2ed94..f0209277 100644 --- a/biosimulators_utils/sedml/exec.py +++ b/biosimulators_utils/sedml/exec.py @@ -195,7 +195,7 @@ def exec_task(task, variables, preprocessed_task=None, log=None, config=None, ** # The preprocessed task was not created if there was no set_value_executer, so create one now: if not preprocessed_task and preprocessed_task_executer: - preprocessed_task_sub_executer() + preprocessed_task = preprocessed_task_sub_executer() # execute task if isinstance(task, Task): From b99416c6f34f63c844d62e4b07beb6a61174c6e6 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Mon, 24 Jul 2023 10:36:04 -0700 Subject: [PATCH 16/16] Update version number. --- biosimulators_utils/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biosimulators_utils/_version.py b/biosimulators_utils/_version.py index 17c53dbd..27a98350 100644 --- a/biosimulators_utils/_version.py +++ b/biosimulators_utils/_version.py @@ -1 +1 @@ -__version__ = '0.1.182' +__version__ = '0.1.183'