From 34c3606d0dba7242b4a4113feb7198ed73e4dcb7 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Wed, 26 Jul 2023 12:44:13 -0700 Subject: [PATCH 1/4] Split handling of attribute changes. The previous implementation would let you either perform 'attribute changes' on actual attributes, or elements, but not both. This splits up the duties so you can do both. --- biosimulators_utils/sedml/utils.py | 161 +++++++++++++++++------------ 1 file changed, 96 insertions(+), 65 deletions(-) diff --git a/biosimulators_utils/sedml/utils.py b/biosimulators_utils/sedml/utils.py index 0f4b0326..80ce7623 100644 --- a/biosimulators_utils/sedml/utils.py +++ b/biosimulators_utils/sedml/utils.py @@ -435,6 +435,7 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non """ # First pass: Must-be-XML changes: + non_xml_changes = [] for change in model.changes: if isinstance(change, AddElementModelChange): parents = eval_xpath(model_etree, change.target, change.target_namespaces) @@ -480,50 +481,31 @@ 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 - - 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, ModelAttributeChange): + obj_xpath, sep, attr = change.target.rpartition('/@') + if sep != '/@': + change.model = model + non_xml_changes.append(change) + continue + # 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 @@ -540,28 +522,77 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non else: new_value = str(new_value) - if set_value_executer: - set_value_executer(change.model, change.target, change.symbol, new_value, preprocessed_task) + # get object to change + obj_xpath, sep, attr = change.target.rpartition('/@') + if sep != '/@': + #Save this for the next pass: + change.model = model + change.new_value = new_value + non_xml_changes.append(change) + continue + 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) + # get object to change + obj_xpath, sep, attr = change.target.rpartition('/@') + if sep != '/@': + continue + 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) + + 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 preprocessed_task_sub_executer: + model_etree.write(model.source, + xml_declaration=True, + encoding="utf-8", + standalone=False, + pretty_print=True) + + preprocessed_task = preprocessed_task_sub_executer() + + # Second pass: changes that need to be interpreter-based: + for change in non_xml_changes: + if isinstance(change, ModelAttributeChange): + if not set_value_executer: + raise NotImplementedError('target ' + change.target + ' cannot be changed by XML manipulation, as the target ' + 'is not an attribute of a model element') 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) + set_value_executer(change.model, change.target, None, change.new_value, preprocessed_task) + + elif isinstance(change, ComputeModelChange): + obj_xpath, sep, attr = change.target.rpartition('/@') + if not set_value_executer: + raise NotImplementedError('target ' + change.target + ' cannot be changed by XML manipulation, as the target ' + 'is not an attribute of a model element') + set_value_executer(change.model, change.target, change.symbol, change.new_value, preprocessed_task) return preprocessed_task From 73f9f232279ee24c6dd2521c139918341305de96 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Wed, 26 Jul 2023 12:44:46 -0700 Subject: [PATCH 2/4] 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 c6b15013..720a2bb1 100644 --- a/biosimulators_utils/_version.py +++ b/biosimulators_utils/_version.py @@ -1 +1 @@ -__version__ = '0.1.184' +__version__ = '0.1.185' From 5f73feffb1d4eb55981870d3e63f8ce1aa3721c2 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Wed, 26 Jul 2023 12:53:28 -0700 Subject: [PATCH 3/4] Lint fixes. --- biosimulators_utils/sedml/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/biosimulators_utils/sedml/utils.py b/biosimulators_utils/sedml/utils.py index 80ce7623..7637c6a9 100644 --- a/biosimulators_utils/sedml/utils.py +++ b/biosimulators_utils/sedml/utils.py @@ -525,7 +525,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 != '/@': - #Save this for the next pass: + # Save this for the next pass: change.model = model change.new_value = new_value non_xml_changes.append(change) @@ -586,7 +586,7 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non 'is not an attribute of a model element') else: set_value_executer(change.model, change.target, None, change.new_value, preprocessed_task) - + elif isinstance(change, ComputeModelChange): obj_xpath, sep, attr = change.target.rpartition('/@') if not set_value_executer: From 580ec7e20060165d0c4c09035dc8afd032c23365 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Wed, 26 Jul 2023 13:23:12 -0700 Subject: [PATCH 4/4] Delete errant code. --- biosimulators_utils/sedml/utils.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/biosimulators_utils/sedml/utils.py b/biosimulators_utils/sedml/utils.py index 7637c6a9..d9e28dd8 100644 --- a/biosimulators_utils/sedml/utils.py +++ b/biosimulators_utils/sedml/utils.py @@ -544,24 +544,6 @@ def apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=Non # change value for obj in objs: obj.set(attr, new_value) - # get object to change - obj_xpath, sep, attr = change.target.rpartition('/@') - if sep != '/@': - continue - 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) else: raise NotImplementedError('Change{} of type {} is not supported.'.format(