From 06a9e8727a801df48ec1450fe5ff9c844199ba91 Mon Sep 17 00:00:00 2001 From: nanglo123 Date: Wed, 2 Oct 2024 15:45:44 -0400 Subject: [PATCH 1/2] Create a copy of the unit dictionary --- mira/metamodel/units.py | 11 ++++++----- tests/test_modeling/test_amr_ops.py | 7 +++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/mira/metamodel/units.py b/mira/metamodel/units.py index 31c2af9f..1b28ec2f 100644 --- a/mira/metamodel/units.py +++ b/mira/metamodel/units.py @@ -44,11 +44,12 @@ class Unit(BaseModel): def from_json(cls, data: Dict[str, Any]) -> "Unit": # Use get_sympy from amr.petrinet, but avoid circular import from mira.sources.amr.petrinet import get_sympy - data["expression"] = get_sympy(data, local_dict=UNIT_SYMBOLS) - assert data.get('expression') is None or not isinstance( - data['expression'], str - ) - return cls(**data) + new_data = data.copy() + new_data["expression"] = get_sympy(data, local_dict=UNIT_SYMBOLS) + assert (new_data.get('expression') is None or + not isinstance(new_data.get('expression'), str)) + + return cls(**new_data) @classmethod def model_validate(cls, obj): diff --git a/tests/test_modeling/test_amr_ops.py b/tests/test_modeling/test_amr_ops.py index e194ed27..666534af 100644 --- a/tests/test_modeling/test_amr_ops.py +++ b/tests/test_modeling/test_amr_ops.py @@ -1,8 +1,10 @@ import unittest import requests from copy import deepcopy as _d + from mira.modeling.amr.ops import * from mira.metamodel.io import mathml_to_expression +from mira.metamodel import safe_parse_expr try: import sbmlmath @@ -270,8 +272,9 @@ def test_replace_parameter_id(self): self.assertEqual(old_param_dict[old_id]['value'], new_param_dict[new_id]['value']) self.assertEqual(old_param_dict[old_id]['distribution'], new_param_dict[new_id]['distribution']) - self.assertEqual(str(old_param_dict[old_id]['units']['expression']), - new_param_dict[new_id]['units']['expression']) + self.assertEqual(safe_parse_expr(old_param_dict[old_id]['units']['expression']), + safe_parse_expr(new_param_dict[new_id]['units'][ + 'expression'])) self.assertEqual(mathml_to_expression(old_param_dict[old_id]['units']['expression_mathml']), mathml_to_expression(new_param_dict[new_id]['units']['expression_mathml'])) From d417351cfe711142e098c6600af83de96fd46e6f Mon Sep 17 00:00:00 2001 From: Ben Gyori Date: Thu, 3 Oct 2024 16:30:51 -0400 Subject: [PATCH 2/2] Address more issues with from_json --- mira/metamodel/template_model.py | 4 ++++ mira/metamodel/templates.py | 6 ++++++ mira/metamodel/units.py | 4 ++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/mira/metamodel/template_model.py b/mira/metamodel/template_model.py index 8570ced4..fc59a7ec 100644 --- a/mira/metamodel/template_model.py +++ b/mira/metamodel/template_model.py @@ -1,3 +1,5 @@ +import copy + from pydantic import ConfigDict __all__ = [ @@ -473,6 +475,8 @@ def from_json(cls, data) -> "TemplateModel": : Returns the newly created template model. """ + # Do a copy just to make sure we don't modify the original data + data = copy.deepcopy(data) local_symbols = {p: sympy.Symbol(p) for p in data.get("parameters", [])} for template_dict in data.get("templates", []): # We need to figure out the template class based on the type diff --git a/mira/metamodel/templates.py b/mira/metamodel/templates.py index df477822..88523a00 100644 --- a/mira/metamodel/templates.py +++ b/mira/metamodel/templates.py @@ -3,6 +3,8 @@ Regenerate the JSON schema by running ``python -m mira.metamodel.schema``. """ +import copy + from pydantic import ConfigDict from typing import Literal @@ -376,6 +378,8 @@ def from_json(cls, data) -> "Concept": if isinstance(data, Concept): return data elif data.get('units'): + # Copy so we don't update the input + data = copy.deepcopy(data) data['units'] = Unit.from_json(data['units']) return cls(**data) @@ -413,6 +417,8 @@ def from_json(cls, data, rate_symbols=None) -> "Template": : A Template object """ + # Make a copy to make sure we don't update the input + data = copy.deepcopy(data) # We make sure to use data such that it's not modified in place, # e.g., we don't use pop or overwrite items, otherwise this function # would have unintended side effects. diff --git a/mira/metamodel/units.py b/mira/metamodel/units.py index 1b28ec2f..ad87d049 100644 --- a/mira/metamodel/units.py +++ b/mira/metamodel/units.py @@ -42,8 +42,8 @@ class Unit(BaseModel): @classmethod def from_json(cls, data: Dict[str, Any]) -> "Unit": - # Use get_sympy from amr.petrinet, but avoid circular import - from mira.sources.amr.petrinet import get_sympy + # Use get_sympy from sources, but avoid circular import + from mira.sources.util import get_sympy new_data = data.copy() new_data["expression"] = get_sympy(data, local_dict=UNIT_SYMBOLS) assert (new_data.get('expression') is None or