From 39f308c002e75c6eba2682e3508ba15960e1a1cc Mon Sep 17 00:00:00 2001 From: rozyczko Date: Fri, 4 Sep 2026 18:23:32 +0200 Subject: [PATCH 1/5] DescriptorBase now is a ModelBase class --- src/easyscience/fitting/multi_fitter.py | 6 +- .../variable/descriptor_any_type.py | 4 +- src/easyscience/variable/descriptor_array.py | 4 +- src/easyscience/variable/descriptor_base.py | 106 +++++++++--------- src/easyscience/variable/descriptor_number.py | 4 +- src/easyscience/variable/parameter.py | 6 +- tests/unit/fitting/test_multi_fitter.py | 4 +- tests/unit/io/test_serializer_dict.py | 8 +- tests/unit/variable/test_descriptor_base.py | 35 ++++++ ...test_parameter_dependency_serialization.py | 2 +- 10 files changed, 112 insertions(+), 67 deletions(-) diff --git a/src/easyscience/fitting/multi_fitter.py b/src/easyscience/fitting/multi_fitter.py index c2db4c25..a63537aa 100644 --- a/src/easyscience/fitting/multi_fitter.py +++ b/src/easyscience/fitting/multi_fitter.py @@ -6,6 +6,7 @@ import numpy as np from ..base_classes import EasyList +from ..base_classes import ModelBase from .fitter import Fitter from .minimizers import FitResults @@ -31,7 +32,10 @@ def __init__( # Aggregate the fit objects so a single object can be sent to Fitter. # *-unpacking keeps any sequence (list, tuple, etc) working, as the # old CollectionBase container did. - self._fit_objects = EasyList(*fit_objects) + # Only ModelBase members are accepted: EasyList harvests parameters + # from ModelBase items alone, so any other NewBase (a bare Parameter, + # say) would be accepted and then silently sit out the fit. + self._fit_objects = EasyList(*fit_objects, protected_types=ModelBase) self._fit_functions = fit_functions # Initialize with the first of the fit_functions, without this it is # not possible to change the fitting engine. diff --git a/src/easyscience/variable/descriptor_any_type.py b/src/easyscience/variable/descriptor_any_type.py index 157117d8..d8830f1d 100644 --- a/src/easyscience/variable/descriptor_any_type.py +++ b/src/easyscience/variable/descriptor_any_type.py @@ -99,7 +99,7 @@ def __repr__(self) -> str: return f"<{self.__class__.__name__} '{self._name}': {value_repr}>" - def as_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: - raw_dict = super().as_dict(skip=skip) + def to_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: + raw_dict = super().to_dict(skip=skip) raw_dict['value'] = self._value return raw_dict diff --git a/src/easyscience/variable/descriptor_array.py b/src/easyscience/variable/descriptor_array.py index 35f1c022..e3542122 100644 --- a/src/easyscience/variable/descriptor_array.py +++ b/src/easyscience/variable/descriptor_array.py @@ -472,14 +472,14 @@ def __repr__(self) -> str: string = string.replace('\n', ',') return string - def as_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: + def to_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: """ Dict representation of the current DescriptorArray. The dict contains the value, unit and variances, in addition to the properties of DescriptorBase. """ - raw_dict = super().as_dict(skip=skip) + raw_dict = super().to_dict(skip=skip) raw_dict['value'] = self._array.values raw_dict['unit'] = str(self._array.unit) raw_dict['variance'] = self._array.variances diff --git a/src/easyscience/variable/descriptor_base.py b/src/easyscience/variable/descriptor_base.py index ba7198bc..273efeb3 100644 --- a/src/easyscience/variable/descriptor_base.py +++ b/src/easyscience/variable/descriptor_base.py @@ -5,14 +5,17 @@ import abc from typing import Any +from typing import Dict +from typing import List from typing import Optional from easyscience import global_object +from easyscience.base_classes.new_base import NewBase from easyscience.global_object.undo_redo import property_stack -from easyscience.io import SerializerComponent +from easyscience.io.serializer_base import SerializerBase -class DescriptorBase(SerializerComponent, metaclass=abc.ABCMeta): +class DescriptorBase(NewBase, metaclass=abc.ABCMeta): """ This is the base of all variable descriptions for models. @@ -74,17 +77,14 @@ def __init__( has an invalid type. """ - if unique_name is None: - unique_name = global_object.generate_unique_name(self.__class__.__name__) - self._unique_name = unique_name - if not isinstance(name, str): raise TypeError('Name must be a string') - self._name: str = name - if display_name is not None and not isinstance(display_name, str): - raise TypeError('Display name must be a string or None') - self._display_name: str = display_name + # Registers the descriptor with the global object map and takes + # care of `unique_name` and `display_name`. + super().__init__(unique_name=unique_name, display_name=display_name) + + self._name: str = name if description is not None and not isinstance(description, str): raise TypeError('Description must be a string or None') @@ -98,9 +98,7 @@ def __init__( url = '' self._url: str = url - # Let the collective know we've been assimilated self._parent = parent - global_object.map.add_vertex(self, obj_type='created') # Make the connection between self and parent if parent is not None: global_object.map.add_edge(parent, self) @@ -142,6 +140,9 @@ def display_name(self) -> str: """ Get a pretty display name. + Unlike ``NewBase`` the fallback is the ``name`` of the + descriptor rather than its ``unique_name``. + Returns ------- str @@ -234,40 +235,6 @@ def url(self, url: Optional[str]) -> None: raise TypeError('url must be a string') self._url = url - @property - def unique_name(self) -> str: - """ - Get the unique name of this object. - - Returns - ------- - str - Unique name of this object. - """ - return self._unique_name - - @unique_name.setter - def unique_name(self, new_unique_name: str): - """ - Set a new unique name for the object. - - The old name is still kept in the map. - - Parameters - ---------- - new_unique_name : str - New unique name for the object. - - Raises - ------ - TypeError - If ``new_unique_name`` is not a string. - """ - if not isinstance(new_unique_name, str): - raise TypeError('Unique name has to be a string.') - self._unique_name = new_unique_name - global_object.map.add_vertex(self) - @property @abc.abstractmethod def value(self) -> Any: @@ -282,8 +249,45 @@ def value(self, value: Any) -> None: def __repr__(self) -> str: """Return printable representation of the object.""" - def __copy__(self) -> DescriptorBase: - """Return a copy of the object.""" - temp = self.as_dict(skip=['unique_name']) - new_obj = self.__class__.from_dict(temp) - return new_obj + def to_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: + """ + Convert a descriptor into a full dictionary using + ``SerializerBase``s generic ``convert_to_dict`` method. + + Unlike ``NewBase.to_dict`` neither ``unique_name`` nor + ``display_name`` is dropped when it was not supplied + explicitly. + + Parameters + ---------- + skip : Optional[List[str]], default=None + List of field names as strings to skip when forming the + dictionary. By default, None. + + Returns + ------- + Dict[str, Any] + Encoded object containing all information to + get back the descriptor. + """ + if skip is None: + skip = [] + return SerializerBase()._convert_to_dict(self, skip=skip, full_encode=False) + + def as_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: + """ + Alias of ``to_dict``, kept for backwards compatibility. + + Parameters + ---------- + skip : Optional[List[str]], default=None + List of field names as strings to skip when forming the + dictionary. By default, None. + + Returns + ------- + Dict[str, Any] + Encoded object containing all information to reform the + descriptor. + """ + return self.to_dict(skip=skip) diff --git a/src/easyscience/variable/descriptor_number.py b/src/easyscience/variable/descriptor_number.py index 5bcbcb0d..2aba019f 100644 --- a/src/easyscience/variable/descriptor_number.py +++ b/src/easyscience/variable/descriptor_number.py @@ -444,8 +444,8 @@ def __repr__(self) -> str: return string # return f"<{class_name} '{obj_name}': {obj_value:0.04f}{obj_unit}>" - def as_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: - raw_dict = super().as_dict(skip=skip) + def to_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: + raw_dict = super().to_dict(skip=skip) raw_dict['value'] = self._scalar.value raw_dict['unit'] = str(self._scalar.unit) raw_dict['variance'] = self._scalar.variance diff --git a/src/easyscience/variable/parameter.py b/src/easyscience/variable/parameter.py index 3ec654f7..3e5095c8 100644 --- a/src/easyscience/variable/parameter.py +++ b/src/easyscience/variable/parameter.py @@ -903,11 +903,11 @@ def free(self) -> bool: def free(self, value: bool) -> None: self.fixed = not value - def as_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: + def to_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: """ - Overwrite the as_dict method to handle dependency information. + Overwrite the to_dict method to handle dependency information. """ - raw_dict = super().as_dict(skip=skip) + raw_dict = super().to_dict(skip=skip) # Add dependency information for dependent parameters if not self._independent: diff --git a/tests/unit/fitting/test_multi_fitter.py b/tests/unit/fitting/test_multi_fitter.py index 97e51c1d..4e3eeaf4 100644 --- a/tests/unit/fitting/test_multi_fitter.py +++ b/tests/unit/fitting/test_multi_fitter.py @@ -290,7 +290,9 @@ def test_rejects_foreign_object(self): MultiFitter([Line(1.0, 0.5), 'not a model'], [None, None]) def test_rejects_bare_parameter(self): - """CollectionBase accepted bare parameters; EasyList does not.""" + """CollectionBase accepted bare parameters; the ModelBase-gated + EasyList does not (a Parameter is a NewBase, but EasyList only + harvests parameters from ModelBase members).""" model = Line(1.0, 0.5) with pytest.raises(TypeError, match='Items must be one of'): MultiFitter([model, Parameter('p', 1.0)], [model, None]) diff --git a/tests/unit/io/test_serializer_dict.py b/tests/unit/io/test_serializer_dict.py index 1ddf21ef..5df61f7c 100644 --- a/tests/unit/io/test_serializer_dict.py +++ b/tests/unit/io/test_serializer_dict.py @@ -50,7 +50,7 @@ def test_variable_SerializerDict(dp_kwargs: dict, dp_cls: Type[DescriptorNumber] if not isinstance(skip, list): skip = [skip] - enc = obj.encode(skip=skip, encoder=SerializerDict) + enc = SerializerDict().encode(obj, skip=skip) expected_keys = set(dp_kwargs.keys()) obtained_keys = set(enc.keys()) @@ -71,9 +71,9 @@ def test_variable_SerializerDict_decode(dp_kwargs: dict, dp_cls: Type[Descriptor obj = dp_cls(**data_dict) - enc = obj.encode(encoder=SerializerDict) + enc = SerializerDict().encode(obj) global_object.map._clear() - dec = dp_cls.decode(enc, decoder=SerializerDict) + dec = SerializerDict.decode(enc) for k in data_dict.keys(): if hasattr(obj, k) and hasattr(dec, k): @@ -88,7 +88,7 @@ def test_variable_SerializerDict_from_dict(dp_kwargs: dict, dp_cls: Type[Descrip obj = dp_cls(**data_dict) - enc = obj.encode(encoder=SerializerDict) + enc = SerializerDict().encode(obj) global_object.map._clear() dec = dp_cls.from_dict(enc) diff --git a/tests/unit/variable/test_descriptor_base.py b/tests/unit/variable/test_descriptor_base.py index 24c342dc..b747e5be 100644 --- a/tests/unit/variable/test_descriptor_base.py +++ b/tests/unit/variable/test_descriptor_base.py @@ -4,6 +4,10 @@ import pytest from easyscience import global_object +from easyscience.base_classes import EasyList +from easyscience.base_classes import NewBase +from easyscience.io import SerializerComponent +from easyscience.variable import DescriptorNumber from easyscience.variable.descriptor_base import DescriptorBase @@ -223,3 +227,34 @@ def test_unique_name_change_exception(self, input, descriptor: DescriptorBase): # When Then Expect with pytest.raises(TypeError): descriptor.unique_name = input + + def test_is_a_new_base(self, descriptor: DescriptorBase): + # When Then Expect + assert isinstance(descriptor, NewBase) + assert not isinstance(descriptor, SerializerComponent) + + def test_as_dict_is_an_alias_of_to_dict(self, descriptor: DescriptorBase): + # When Then Expect + assert descriptor.as_dict() == descriptor.to_dict() + assert descriptor.as_dict(skip=['url']) == descriptor.to_dict(skip=['url']) + + def test_to_dict_keeps_generated_unique_name(self, clear): + """``NewBase.to_dict`` drops a generated unique_name, a + descriptor must not: parameter dependencies and serialized + models refer to descriptors by unique_name.""" + # When + descriptor = DescriptorNumber(name='name', value=1.0) + + # Then Expect + assert descriptor._default_unique_name + assert descriptor.to_dict()['unique_name'] == descriptor.unique_name + + def test_can_be_held_by_an_easy_list(self, clear): + """Descriptors are NewBase objects, so EasyList accepts them.""" + # When + descriptor = DescriptorNumber(name='name', value=1.0) + easy_list = EasyList(descriptor) + + # Then Expect + assert list(easy_list) == [descriptor] + assert easy_list[descriptor.unique_name] is descriptor diff --git a/tests/unit/variable/test_parameter_dependency_serialization.py b/tests/unit/variable/test_parameter_dependency_serialization.py index 62feb5f3..0dddc5d5 100644 --- a/tests/unit/variable/test_parameter_dependency_serialization.py +++ b/tests/unit/variable/test_parameter_dependency_serialization.py @@ -397,7 +397,7 @@ def test_backward_compatibility_base_deserializer(self, clear_global_map): ) # Use base serializer path (SerializerDict.decode) - serialized = b.encode(encoder=SerializerDict) + serialized = SerializerDict().encode(b) global_object.map._clear() # This should not raise the "_independent" error anymore From fda0d2863dde72756ae9209cdddda1501f0bed7f Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Fri, 4 Sep 2026 21:25:23 +0200 Subject: [PATCH 2/5] updated handling of unique_names --- src/easyscience/variable/descriptor_base.py | 28 +-------------------- tests/unit/variable/test_descriptor_base.py | 19 ++++++++++---- 2 files changed, 15 insertions(+), 32 deletions(-) diff --git a/src/easyscience/variable/descriptor_base.py b/src/easyscience/variable/descriptor_base.py index 273efeb3..d59db944 100644 --- a/src/easyscience/variable/descriptor_base.py +++ b/src/easyscience/variable/descriptor_base.py @@ -12,7 +12,6 @@ from easyscience import global_object from easyscience.base_classes.new_base import NewBase from easyscience.global_object.undo_redo import property_stack -from easyscience.io.serializer_base import SerializerBase class DescriptorBase(NewBase, metaclass=abc.ABCMeta): @@ -249,34 +248,9 @@ def value(self, value: Any) -> None: def __repr__(self) -> str: """Return printable representation of the object.""" - def to_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: - """ - Convert a descriptor into a full dictionary using - ``SerializerBase``s generic ``convert_to_dict`` method. - - Unlike ``NewBase.to_dict`` neither ``unique_name`` nor - ``display_name`` is dropped when it was not supplied - explicitly. - - Parameters - ---------- - skip : Optional[List[str]], default=None - List of field names as strings to skip when forming the - dictionary. By default, None. - - Returns - ------- - Dict[str, Any] - Encoded object containing all information to - get back the descriptor. - """ - if skip is None: - skip = [] - return SerializerBase()._convert_to_dict(self, skip=skip, full_encode=False) - def as_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: """ - Alias of ``to_dict``, kept for backwards compatibility. + Alias of ``NewBase.to_dict``, kept for backwards compatibility. Parameters ---------- diff --git a/tests/unit/variable/test_descriptor_base.py b/tests/unit/variable/test_descriptor_base.py index b747e5be..c4498cf4 100644 --- a/tests/unit/variable/test_descriptor_base.py +++ b/tests/unit/variable/test_descriptor_base.py @@ -238,16 +238,25 @@ def test_as_dict_is_an_alias_of_to_dict(self, descriptor: DescriptorBase): assert descriptor.as_dict() == descriptor.to_dict() assert descriptor.as_dict(skip=['url']) == descriptor.to_dict(skip=['url']) - def test_to_dict_keeps_generated_unique_name(self, clear): - """``NewBase.to_dict`` drops a generated unique_name, a - descriptor must not: parameter dependencies and serialized - models refer to descriptors by unique_name.""" + def test_to_dict_drops_generated_unique_name(self, clear): + """Descriptors follow the ``NewBase`` design: an auto-generated + unique_name is not serialized, so a decoded descriptor is given + a fresh one instead of colliding with the original.""" # When descriptor = DescriptorNumber(name='name', value=1.0) # Then Expect assert descriptor._default_unique_name - assert descriptor.to_dict()['unique_name'] == descriptor.unique_name + assert 'unique_name' not in descriptor.to_dict() + + def test_to_dict_keeps_explicit_unique_name(self, clear): + """An explicitly supplied unique_name is still serialized.""" + # When + descriptor = DescriptorNumber(name='name', value=1.0, unique_name='explicit_name') + + # Then Expect + assert not descriptor._default_unique_name + assert descriptor.to_dict()['unique_name'] == 'explicit_name' def test_can_be_held_by_an_easy_list(self, clear): """Descriptors are NewBase objects, so EasyList accepts them.""" From c02814f5f58db5c2479abc06fccaa198dc44ef78 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Fri, 4 Sep 2026 22:24:58 +0200 Subject: [PATCH 3/5] updated docstrings and some docs --- docs/docs/api-reference/index.md | 8 +++++--- src/easyscience/base_classes/easy_list.py | 5 ++++- src/easyscience/base_classes/new_base.py | 8 ++++++++ src/easyscience/fitting/multi_fitter.py | 15 +++++++++++++++ src/easyscience/variable/descriptor_base.py | 16 ++++++++++++++++ 5 files changed, 48 insertions(+), 4 deletions(-) diff --git a/docs/docs/api-reference/index.md b/docs/docs/api-reference/index.md index 4bd6f561..c6100bfe 100644 --- a/docs/docs/api-reference/index.md +++ b/docs/docs/api-reference/index.md @@ -8,8 +8,9 @@ This section contains the reference detailing the functions and modules available in EasyScience. - [base_classes](base_classes.md) – Core abstract and helper base - classes used to build EasyScience objects (e.g. `ObjBase`, - `ModelBase`). + classes used to build EasyScience objects (e.g. `NewBase`, + `ModelBase`, `EasyList`; the legacy `ObjBase` and `CollectionBase` are + deprecated). - [fitting](fitting.md) – Fitting utilities and interfaces, including `Fitter` and available minimizers. - [global_object](global_object.md) – Global singleton providing shared @@ -23,4 +24,5 @@ available in EasyScience. - [utils](utils.md) – Miscellaneous utility functions and helpers (class tools, decorators, type helpers). - [variable](variable.md) – Descriptor types and variable abstractions - (e.g. `DescriptorNumber`, `Parameter`, `DescriptorArray`). + (e.g. `DescriptorNumber`, `Parameter`, `DescriptorArray`). All of them + are `NewBase` objects, serialized with `to_dict`/`from_dict`. diff --git a/src/easyscience/base_classes/easy_list.py b/src/easyscience/base_classes/easy_list.py index 23ac673d..f1f60acd 100644 --- a/src/easyscience/base_classes/easy_list.py +++ b/src/easyscience/base_classes/easy_list.py @@ -45,7 +45,10 @@ def __init__( Initial items to add to the list. protected_types : list[Type[NewBase]] | Type[NewBase] | None, default=None Types that are allowed in the list. Can be a single NewBase - subclass or a list of them. If None,. By default, None. + subclass or a list of them. If None, any ``NewBase`` object + is accepted, including descriptors and parameters. Note that + only ``ModelBase`` items contribute to ``get_all_variables`` + and hence to fitting. By default, None. unique_name : Optional[str], default=None Optional unique name for the list. By default, None. display_name : Optional[str], default=None diff --git a/src/easyscience/base_classes/new_base.py b/src/easyscience/base_classes/new_base.py index 115677f1..106e6f3c 100644 --- a/src/easyscience/base_classes/new_base.py +++ b/src/easyscience/base_classes/new_base.py @@ -137,6 +137,14 @@ def to_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: Dict[str, Any] Encoded object containing all information to reform an EasyScience object. + + Notes + ----- + A ``unique_name`` that was generated automatically is not + written; only an explicitly supplied one is, so that a + deserialized object gets a fresh name instead of clashing with + the original. Likewise ``display_name`` is omitted when it is + ``None``. Pass ``skip`` to drop further fields. """ serializer = SerializerBase() if skip is None: diff --git a/src/easyscience/fitting/multi_fitter.py b/src/easyscience/fitting/multi_fitter.py index a63537aa..6c70074e 100644 --- a/src/easyscience/fitting/multi_fitter.py +++ b/src/easyscience/fitting/multi_fitter.py @@ -29,6 +29,21 @@ def __init__( fit_objects: list | None = None, fit_functions: list[Callable] | None = None, ): + """ + Set up a fitter for several models and datasets at once. + + Parameters + ---------- + fit_objects : list | None, default=None + ``ModelBase`` objects to fit, one per dataset. Any sequence + is accepted; each element must be a ``ModelBase`` instance. + By default, None. + fit_functions : list[Callable] | None, default=None + Fit functions, one per fit object and in the same order. The + first one is used to initialise the underlying ``Fitter``. + By default, None. + + """ # Aggregate the fit objects so a single object can be sent to Fitter. # *-unpacking keeps any sequence (list, tuple, etc) working, as the # old CollectionBase container did. diff --git a/src/easyscience/variable/descriptor_base.py b/src/easyscience/variable/descriptor_base.py index d59db944..452752e1 100644 --- a/src/easyscience/variable/descriptor_base.py +++ b/src/easyscience/variable/descriptor_base.py @@ -27,6 +27,22 @@ class DescriptorBase(NewBase, metaclass=abc.ABCMeta): A ``Descriptor`` is typically something which describes part of a model and is non-fittable and generally changes the state of an object. + + ``DescriptorBase`` is a ``NewBase`` object. As such every descriptor + is registered in the global object map under its ``unique_name``, + has an optional ``display_name`` and is serialized with + ``to_dict``/``from_dict``. Descriptors and parameters can + therefore be held directly by an ``EasyList``. + + Following the ``NewBase`` design, a ``unique_name`` that was + generated automatically is *not* written by ``to_dict``; a + deserialized descriptor is simply assigned a fresh one. Only a + ``unique_name`` passed explicitly to the constructor is serialized. + + Descriptors no longer provide the ``SerializerComponent`` methods + ``encode``, ``decode`` and ``encode_data``. Use a serializer + directly instead, e.g. ``SerializerDict().encode(descriptor)`` and + ``SerializerDict.decode(data)``. """ _global_object = global_object From 4c3a8b4e243fb464d781800826e56830e4b3cad6 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Fri, 11 Sep 2026 12:56:54 +0200 Subject: [PATCH 4/5] PR issues addressed --- docs/docs/api-reference/index.md | 3 +- src/easyscience/base_classes/based_base.py | 6 +-- src/easyscience/io/serializer_base.py | 4 +- src/easyscience/io/serializer_component.py | 4 +- src/easyscience/job/theoreticalmodel.py | 4 +- .../variable/descriptor_any_type.py | 8 ++-- src/easyscience/variable/descriptor_array.py | 12 ++--- src/easyscience/variable/descriptor_base.py | 47 +------------------ src/easyscience/variable/descriptor_bool.py | 3 -- src/easyscience/variable/descriptor_number.py | 8 ++-- src/easyscience/variable/descriptor_str.py | 3 -- src/easyscience/variable/parameter.py | 5 -- .../unit/base_classes/test_collection_base.py | 8 ++-- tests/unit/base_classes/test_obj_base.py | 10 ++-- .../test_integration_comprehensive.py | 4 +- tests/unit/io/test_serializer_base.py | 2 +- tests/unit/io/test_serializer_component.py | 4 +- tests/unit/io/test_serializer_dict.py | 4 +- .../unit/variable/test_descriptor_any_type.py | 1 - tests/unit/variable/test_descriptor_array.py | 8 ---- tests/unit/variable/test_descriptor_base.py | 45 ------------------ tests/unit/variable/test_descriptor_bool.py | 2 - tests/unit/variable/test_descriptor_number.py | 5 -- tests/unit/variable/test_descriptor_str.py | 2 - tests/unit/variable/test_parameter.py | 3 -- ...test_parameter_dependency_serialization.py | 30 ++++++------ 26 files changed, 53 insertions(+), 182 deletions(-) diff --git a/docs/docs/api-reference/index.md b/docs/docs/api-reference/index.md index c6100bfe..1a035c29 100644 --- a/docs/docs/api-reference/index.md +++ b/docs/docs/api-reference/index.md @@ -9,8 +9,7 @@ available in EasyScience. - [base_classes](base_classes.md) – Core abstract and helper base classes used to build EasyScience objects (e.g. `NewBase`, - `ModelBase`, `EasyList`; the legacy `ObjBase` and `CollectionBase` are - deprecated). + `ModelBase`, `EasyList`). - [fitting](fitting.md) – Fitting utilities and interfaces, including `Fitter` and available minimizers. - [global_object](global_object.md) – Global singleton providing shared diff --git a/src/easyscience/base_classes/based_base.py b/src/easyscience/base_classes/based_base.py index 4044fb7b..7070f93a 100644 --- a/src/easyscience/base_classes/based_base.py +++ b/src/easyscience/base_classes/based_base.py @@ -239,11 +239,11 @@ def __dir__(self) -> Iterable[str]: def __copy__(self) -> BasedBase: """Return a copy of the object.""" - temp = self.as_dict(skip=['unique_name']) + temp = self.to_dict(skip=['unique_name']) new_obj = self.__class__.from_dict(temp) return new_obj - def as_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: + def to_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: """ Convert an object into a full dictionary using ``SerializerDict``. This is a shortcut for @@ -266,4 +266,4 @@ def as_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: skip = [] if 'unique_name' not in skip: skip.append('unique_name') - return super().as_dict(skip=skip) + return super().to_dict(skip=skip) diff --git a/src/easyscience/io/serializer_base.py b/src/easyscience/io/serializer_base.py index 5d9b0e51..26b1ad72 100644 --- a/src/easyscience/io/serializer_base.py +++ b/src/easyscience/io/serializer_base.py @@ -215,13 +215,13 @@ def runner(o): err = False if err: raise NotImplementedError( - 'Unable to automatically determine as_dict ' + 'Unable to automatically determine to_dict ' 'format from class. MSONAble requires all ' 'args to be present as either self.argname or ' 'self._argname, and kwargs to be present under' 'a self.kwargs variable to automatically ' 'determine the dict format. Alternatively, ' - 'you can implement both as_dict and from_dict.' + 'you can implement both to_dict and from_dict.' ) d[c] = self._recursive_encoder( a, skip=skip, encoder=self, full_encode=full_encode, **kwargs diff --git a/src/easyscience/io/serializer_component.py b/src/easyscience/io/serializer_component.py index 10c5f7aa..9ef079ab 100644 --- a/src/easyscience/io/serializer_component.py +++ b/src/easyscience/io/serializer_component.py @@ -26,7 +26,7 @@ class SerializerComponent: """ def __deepcopy__(self, memo): - return self.from_dict(self.as_dict()) + return self.from_dict(self.to_dict()) def encode( self, skip: Optional[List[str]] = None, encoder: Optional[SerializerBase] = None, **kwargs @@ -82,7 +82,7 @@ def decode(cls, obj: Any, decoder: Optional[SerializerBase] = None) -> Any: decoder = SerializerDict return decoder.decode(obj) - def as_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: + def to_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: """ Convert an EasyScience object into a full dictionary using ``SerializerDict``. This is a shortcut for diff --git a/src/easyscience/job/theoreticalmodel.py b/src/easyscience/job/theoreticalmodel.py index 3fa87e50..3cddec7e 100644 --- a/src/easyscience/job/theoreticalmodel.py +++ b/src/easyscience/job/theoreticalmodel.py @@ -18,6 +18,6 @@ def __init__(self, name: str, *args, **kwargs): def __str__(self): raise NotImplementedError('Copy not implemented') - def as_dict(self, skip: list = []) -> dict: - this_dict = super().as_dict(skip=skip) + def to_dict(self, skip: list = []) -> dict: + this_dict = super().to_dict(skip=skip) return this_dict diff --git a/src/easyscience/variable/descriptor_any_type.py b/src/easyscience/variable/descriptor_any_type.py index d8830f1d..06f52dbf 100644 --- a/src/easyscience/variable/descriptor_any_type.py +++ b/src/easyscience/variable/descriptor_any_type.py @@ -34,7 +34,6 @@ def __init__( description: Optional[str] = None, url: Optional[str] = None, display_name: Optional[str] = None, - parent: Optional[Any] = None, ): """ Constructor for the DescriptorAnyType class. @@ -42,9 +41,9 @@ def __init__( param name: Name of the descriptor param value: Value of the descriptor param description: Description of the descriptor param url: URL of the descriptor param display_name: Display - name of the descriptor param parent: Parent of the descriptor .. - note:: Undo/Redo functionality is implemented for the attributes - ``variance``, ``error``, ``unit`` and ``value``. + name of the descriptor .. note:: Undo/Redo functionality is + implemented for the attributes ``variance``, ``error``, + ``unit`` and ``value``. """ self._value = value @@ -55,7 +54,6 @@ def __init__( description=description, url=url, display_name=display_name, - parent=parent, ) @property diff --git a/src/easyscience/variable/descriptor_array.py b/src/easyscience/variable/descriptor_array.py index e3542122..de6c0dac 100644 --- a/src/easyscience/variable/descriptor_array.py +++ b/src/easyscience/variable/descriptor_array.py @@ -42,7 +42,6 @@ def __init__( description: Optional[str] = None, url: Optional[str] = None, display_name: Optional[str] = None, - parent: Optional[Any] = None, dimensions: Optional[list] = None, ): """ @@ -52,11 +51,11 @@ def __init__( the values of the descriptor param unit: Unit of the descriptor param variance: Variances of the descriptor param description: Description of the descriptor param url: URL of the descriptor - param display_name: Display name of the descriptor param parent: - Parent of the descriptor param dimensions: List of dimensions to - pass to scipp. Will be autogenerated if not supplied. .. note:: - Undo/Redo functionality is implemented for the attributes - ``variance``, ``error``, ``unit`` and ``value``. + param display_name: Display name of the descriptor param + dimensions: List of dimensions to pass to scipp. Will be + autogenerated if not supplied. .. note:: Undo/Redo functionality + is implemented for the attributes ``variance``, ``error``, + ``unit`` and ``value``. """ if not isinstance(value, (list, np.ndarray)): @@ -104,7 +103,6 @@ def __init__( description=description, url=url, display_name=display_name, - parent=parent, ) # Call convert_unit during initialization to ensure that the unit has no numbers in it, and to ensure unit consistency. diff --git a/src/easyscience/variable/descriptor_base.py b/src/easyscience/variable/descriptor_base.py index 452752e1..e2214d3a 100644 --- a/src/easyscience/variable/descriptor_base.py +++ b/src/easyscience/variable/descriptor_base.py @@ -27,27 +27,11 @@ class DescriptorBase(NewBase, metaclass=abc.ABCMeta): A ``Descriptor`` is typically something which describes part of a model and is non-fittable and generally changes the state of an object. - - ``DescriptorBase`` is a ``NewBase`` object. As such every descriptor - is registered in the global object map under its ``unique_name``, - has an optional ``display_name`` and is serialized with - ``to_dict``/``from_dict``. Descriptors and parameters can - therefore be held directly by an ``EasyList``. - - Following the ``NewBase`` design, a ``unique_name`` that was - generated automatically is *not* written by ``to_dict``; a - deserialized descriptor is simply assigned a fresh one. Only a - ``unique_name`` passed explicitly to the constructor is serialized. - - Descriptors no longer provide the ``SerializerComponent`` methods - ``encode``, ``decode`` and ``encode_data``. Use a serializer - directly instead, e.g. ``SerializerDict().encode(descriptor)`` and - ``SerializerDict.decode(data)``. """ _global_object = global_object # Used by serializer - _REDIRECT = {'parent': None} + _REDIRECT = {} def __init__( self, @@ -56,7 +40,6 @@ def __init__( description: Optional[str] = None, url: Optional[str] = None, display_name: Optional[str] = None, - parent: Optional[Any] = None, ): """ This is the base of variables for models. @@ -81,9 +64,6 @@ def __init__( Lookup url for documentation/information. By default, None. display_name : Optional[str], default=None A pretty name for the object. By default, None. - parent : Optional[Any], default=None - The object which this descriptor is attached to. By default, - None. Raises ------ @@ -95,8 +75,6 @@ def __init__( if not isinstance(name, str): raise TypeError('Name must be a string') - # Registers the descriptor with the global object map and takes - # care of `unique_name` and `display_name`. super().__init__(unique_name=unique_name, display_name=display_name) self._name: str = name @@ -113,11 +91,6 @@ def __init__( url = '' self._url: str = url - self._parent = parent - # Make the connection between self and parent - if parent is not None: - global_object.map.add_edge(parent, self) - @property def name(self) -> str: """ @@ -263,21 +236,3 @@ def value(self, value: Any) -> None: @abc.abstractmethod def __repr__(self) -> str: """Return printable representation of the object.""" - - def as_dict(self, skip: Optional[List[str]] = None) -> Dict[str, Any]: - """ - Alias of ``NewBase.to_dict``, kept for backwards compatibility. - - Parameters - ---------- - skip : Optional[List[str]], default=None - List of field names as strings to skip when forming the - dictionary. By default, None. - - Returns - ------- - Dict[str, Any] - Encoded object containing all information to reform the - descriptor. - """ - return self.to_dict(skip=skip) diff --git a/src/easyscience/variable/descriptor_bool.py b/src/easyscience/variable/descriptor_bool.py index 05a09d9d..874509fc 100644 --- a/src/easyscience/variable/descriptor_bool.py +++ b/src/easyscience/variable/descriptor_bool.py @@ -3,7 +3,6 @@ from __future__ import annotations -from typing import Any from typing import Optional from easyscience.global_object.undo_redo import property_stack @@ -22,7 +21,6 @@ def __init__( description: Optional[str] = None, url: Optional[str] = None, display_name: Optional[str] = None, - parent: Optional[Any] = None, ): if not isinstance(value, bool): raise ValueError(f'{value=} must be type bool') @@ -32,7 +30,6 @@ def __init__( description=description, url=url, display_name=display_name, - parent=parent, ) if not isinstance(value, bool): raise TypeError(f'{value=} must be type bool') diff --git a/src/easyscience/variable/descriptor_number.py b/src/easyscience/variable/descriptor_number.py index 2aba019f..c2a97262 100644 --- a/src/easyscience/variable/descriptor_number.py +++ b/src/easyscience/variable/descriptor_number.py @@ -65,7 +65,6 @@ def __init__( description: Optional[str] = None, url: Optional[str] = None, display_name: Optional[str] = None, - parent: Optional[Any] = None, **kwargs: Any, # Additional keyword arguments (used for (de)serialization) ): """ @@ -75,9 +74,9 @@ def __init__( descriptor param unit: Unit of the descriptor param variance: Variance of the descriptor param description: Description of the descriptor param url: URL of the descriptor param display_name: - Display name of the descriptor param parent: Parent of the - descriptor .. note:: Undo/Redo functionality is implemented for - the attributes ``variance``, ``error``, ``unit`` and ``value``. + Display name of the descriptor .. note:: Undo/Redo functionality + is implemented for the attributes ``variance``, ``error``, + ``unit`` and ``value``. """ self._observers: List[DescriptorNumber] = [] @@ -107,7 +106,6 @@ def __init__( description=description, url=url, display_name=display_name, - parent=parent, ) # Call convert_unit during initialization to ensure that the unit has no numbers in it, and to ensure unit consistency. diff --git a/src/easyscience/variable/descriptor_str.py b/src/easyscience/variable/descriptor_str.py index 5e74e786..e4b28823 100644 --- a/src/easyscience/variable/descriptor_str.py +++ b/src/easyscience/variable/descriptor_str.py @@ -3,7 +3,6 @@ from __future__ import annotations -from typing import Any from typing import Optional from easyscience.global_object.undo_redo import property_stack @@ -22,7 +21,6 @@ def __init__( description: Optional[str] = None, url: Optional[str] = None, display_name: Optional[str] = None, - parent: Optional[Any] = None, ): super().__init__( name=name, @@ -30,7 +28,6 @@ def __init__( description=description, url=url, display_name=display_name, - parent=parent, ) if not isinstance(value, str): raise ValueError(f'{value=} must be type str') diff --git a/src/easyscience/variable/parameter.py b/src/easyscience/variable/parameter.py index 3e5095c8..35654ed5 100644 --- a/src/easyscience/variable/parameter.py +++ b/src/easyscience/variable/parameter.py @@ -52,7 +52,6 @@ def __init__( url: Optional[str] = None, display_name: Optional[str] = None, callback: property = property(), - parent: Optional[Any] = None, **kwargs: Any, # Additional keyword arguments (used for (de)serialization) ): """ @@ -92,9 +91,6 @@ def __init__( callback : property, default=property() Callback used to synchronize the parameter with an external model. - parent : Optional[Any], default=None - The object which is the parent to this one. By default, - None. **kwargs : Any Additional keyword arguments used during serialization. @@ -147,7 +143,6 @@ def __init__( description=description, url=url, display_name=display_name, - parent=parent, **kwargs, # Additional keyword arguments (used for (de)serialization) ) diff --git a/tests/unit/base_classes/test_collection_base.py b/tests/unit/base_classes/test_collection_base.py index 732c3acf..8886a474 100644 --- a/tests/unit/base_classes/test_collection_base.py +++ b/tests/unit/base_classes/test_collection_base.py @@ -305,7 +305,7 @@ def test_CollectionBase_dir(cls): 'append', 'unique_name', 'index', - 'as_dict', + 'to_dict', 'clear', 'extend', 'encode', @@ -327,11 +327,11 @@ def test_CollectionBase_dir(cls): @pytest.mark.parametrize('cls', class_constructors) -def test_CollectionBase_as_dict(cls): +def test_CollectionBase_to_dict(cls): name = 'testing' kwargs = {'p1': DescriptorNumber('par1', 1)} obj = cls(name, **kwargs) - d = obj.as_dict() + d = obj.to_dict() def check_dict(dict_1: dict, dict_2: dict): keys_1 = list(dict_1.keys()) @@ -423,7 +423,7 @@ def test_CollectionBase_iterator_dict(cls): l_object = [p1, p2, p3, p4] obj = cls(name, *l_object) - d = obj.as_dict() + d = obj.to_dict() global_object.map._clear() obj2 = cls.from_dict(d) diff --git a/tests/unit/base_classes/test_obj_base.py b/tests/unit/base_classes/test_obj_base.py index 5eb1b2ea..723d617f 100644 --- a/tests/unit/base_classes/test_obj_base.py +++ b/tests/unit/base_classes/test_obj_base.py @@ -141,11 +141,11 @@ def test_ObjBase_fit_objects(setup_pars: dict): pass -def test_ObjBase_as_dict(clear, setup_pars: dict): +def test_ObjBase_to_dict(clear, setup_pars: dict): name = setup_pars['name'] del setup_pars['name'] obj = ObjBase(name, **setup_pars) - obtained = obj.as_dict() + obtained = obj.to_dict() assert isinstance(obtained, dict) expected = { '@module': 'easyscience.legacy.obj_base', @@ -238,7 +238,7 @@ def test_ObjBase_dict_roundtrip(clear, setup_pars: dict): name = setup_pars['name'] del setup_pars['name'] obj = ObjBase(name, **setup_pars, unique_name='special_name') - obj_dict = obj.as_dict() + obj_dict = obj.to_dict() global_object.map._clear() @@ -246,7 +246,7 @@ def test_ObjBase_dict_roundtrip(clear, setup_pars: dict): new_obj = ObjBase.from_dict(obj_dict) # Expect - new_obj_dict = new_obj.as_dict() + new_obj_dict = new_obj.to_dict() assert obj_dict == new_obj_dict @@ -257,7 +257,7 @@ def test_ObjBase_dir(setup_pars): expected = [ 'encode', 'decode', - 'as_dict', + 'to_dict', 'des1', 'des2', 'from_dict', diff --git a/tests/unit/global_object/test_integration_comprehensive.py b/tests/unit/global_object/test_integration_comprehensive.py index 61268954..541c9e46 100644 --- a/tests/unit/global_object/test_integration_comprehensive.py +++ b/tests/unit/global_object/test_integration_comprehensive.py @@ -361,8 +361,8 @@ def test_serialization_integration_with_global_state(self, clear_all): original_vertex_count = len(global_obj.map.vertices()) # When - Serialize objects - param_dict = param.as_dict() - obj_dict = obj.as_dict() + param_dict = param.to_dict() + obj_dict = obj.to_dict() # Clear global state global_obj.map._clear() diff --git a/tests/unit/io/test_serializer_base.py b/tests/unit/io/test_serializer_base.py index c3c8dd21..550c4d75 100644 --- a/tests/unit/io/test_serializer_base.py +++ b/tests/unit/io/test_serializer_base.py @@ -471,7 +471,7 @@ def __init__(self, name: str, missing_param: str = 'default'): obj = MockObjMissingAttrs('test') - with pytest.raises(NotImplementedError, match='Unable to automatically determine as_dict'): + with pytest.raises(NotImplementedError, match='Unable to automatically determine to_dict'): serializer._convert_to_dict(obj) def test_convert_to_dict_with_kwargs_attribute(self, serializer, clear): diff --git a/tests/unit/io/test_serializer_component.py b/tests/unit/io/test_serializer_component.py index c4e21f49..a90e364c 100644 --- a/tests/unit/io/test_serializer_component.py +++ b/tests/unit/io/test_serializer_component.py @@ -71,7 +71,7 @@ def check_dict(check, item): @pytest.mark.parametrize(**skip_dict) @pytest.mark.parametrize(**dp_param_dict) -def test_variable_as_dict_methods(dp_kwargs: dict, dp_cls: Type[DescriptorNumber], skip): +def test_variable_to_dict_methods(dp_kwargs: dict, dp_cls: Type[DescriptorNumber], skip): data_dict = {k: v for k, v in dp_kwargs.items() if k[0] != '@'} obj = dp_cls(**data_dict) @@ -84,7 +84,7 @@ def test_variable_as_dict_methods(dp_kwargs: dict, dp_cls: Type[DescriptorNumber if not isinstance(skip, list): skip = [skip] - enc = obj.as_dict(skip=skip) + enc = obj.to_dict(skip=skip) expected_keys = set(dp_kwargs.keys()) obtained_keys = set(enc.keys()) diff --git a/tests/unit/io/test_serializer_dict.py b/tests/unit/io/test_serializer_dict.py index 5df61f7c..1b37fae9 100644 --- a/tests/unit/io/test_serializer_dict.py +++ b/tests/unit/io/test_serializer_dict.py @@ -106,7 +106,7 @@ def test_group_encode(): from easyscience.base_classes import CollectionBase b = CollectionBase('test', d0, d1) - d = b.as_dict() + d = b.to_dict() assert isinstance(d['data'], list) @@ -117,5 +117,5 @@ def test_group_encode2(): from easyscience.base_classes import CollectionBase b = ObjBase('outer', b=CollectionBase('test', d0, d1)) - d = b.as_dict() + d = b.to_dict() assert isinstance(d['b'], dict) diff --git a/tests/unit/variable/test_descriptor_any_type.py b/tests/unit/variable/test_descriptor_any_type.py index 5dc28b58..12db376a 100644 --- a/tests/unit/variable/test_descriptor_any_type.py +++ b/tests/unit/variable/test_descriptor_any_type.py @@ -17,7 +17,6 @@ def descriptor(self): description='description', url='url', display_name='display_name', - parent=None, ) return descriptor diff --git a/tests/unit/variable/test_descriptor_array.py b/tests/unit/variable/test_descriptor_array.py index 73bb0857..36ede2e3 100644 --- a/tests/unit/variable/test_descriptor_array.py +++ b/tests/unit/variable/test_descriptor_array.py @@ -23,7 +23,6 @@ def descriptor(self): description='description', url='url', display_name='display_name', - parent=None, ) return descriptor @@ -37,7 +36,6 @@ def descriptor_dimensionless(self): description='description', url='url', display_name='display_name', - parent=None, ) return descriptor @@ -67,7 +65,6 @@ def test_init_sc_unit(self): description='description', url='url', display_name='display_name', - parent=None, ) # Expect @@ -86,7 +83,6 @@ def test_init_sc_unit_unknown(self): description='description', url='url', display_name='display_name', - parent=None, ) @pytest.mark.parametrize('value', [True, 'string']) @@ -103,7 +99,6 @@ def test_init_value_type_exception(self, value): description='description', url='url', display_name='display_name', - parent=None, ) def test_init_variance_exception(self): @@ -119,7 +114,6 @@ def test_init_variance_exception(self): description='description', url='url', display_name='display_name', - parent=None, ) # test from_scipp @@ -1481,7 +1475,6 @@ def test_negation(self, descriptor): description='description', url='url', display_name='display_name', - parent=None, ) assert type(result) == DescriptorArray assert result.name == result.unique_name @@ -1500,7 +1493,6 @@ def test_abs(self, descriptor): description='description', url='url', display_name='display_name', - parent=None, ) # Then diff --git a/tests/unit/variable/test_descriptor_base.py b/tests/unit/variable/test_descriptor_base.py index c4498cf4..9661afa3 100644 --- a/tests/unit/variable/test_descriptor_base.py +++ b/tests/unit/variable/test_descriptor_base.py @@ -23,7 +23,6 @@ def descriptor(self): description='description', url='url', display_name='display_name', - parent=None, ) return descriptor @@ -44,7 +43,6 @@ def test_init_name_type_error(self, name): description='description', url='url', display_name='display_name', - parent=None, ) @pytest.mark.parametrize( @@ -60,7 +58,6 @@ def test_init_display_name_type_error(self, display_name): description='description', url='url', display_name=display_name, - parent=None, ) @pytest.mark.parametrize( @@ -76,7 +73,6 @@ def test_init_description_type_error(self, description): description=description, url='url', display_name='display_name', - parent=None, ) @pytest.mark.parametrize( @@ -92,7 +88,6 @@ def test_init_url_type_error(self, url): description='description', url=url, display_name='display_name', - parent=None, ) def test_init(self, descriptor: DescriptorBase): @@ -227,43 +222,3 @@ def test_unique_name_change_exception(self, input, descriptor: DescriptorBase): # When Then Expect with pytest.raises(TypeError): descriptor.unique_name = input - - def test_is_a_new_base(self, descriptor: DescriptorBase): - # When Then Expect - assert isinstance(descriptor, NewBase) - assert not isinstance(descriptor, SerializerComponent) - - def test_as_dict_is_an_alias_of_to_dict(self, descriptor: DescriptorBase): - # When Then Expect - assert descriptor.as_dict() == descriptor.to_dict() - assert descriptor.as_dict(skip=['url']) == descriptor.to_dict(skip=['url']) - - def test_to_dict_drops_generated_unique_name(self, clear): - """Descriptors follow the ``NewBase`` design: an auto-generated - unique_name is not serialized, so a decoded descriptor is given - a fresh one instead of colliding with the original.""" - # When - descriptor = DescriptorNumber(name='name', value=1.0) - - # Then Expect - assert descriptor._default_unique_name - assert 'unique_name' not in descriptor.to_dict() - - def test_to_dict_keeps_explicit_unique_name(self, clear): - """An explicitly supplied unique_name is still serialized.""" - # When - descriptor = DescriptorNumber(name='name', value=1.0, unique_name='explicit_name') - - # Then Expect - assert not descriptor._default_unique_name - assert descriptor.to_dict()['unique_name'] == 'explicit_name' - - def test_can_be_held_by_an_easy_list(self, clear): - """Descriptors are NewBase objects, so EasyList accepts them.""" - # When - descriptor = DescriptorNumber(name='name', value=1.0) - easy_list = EasyList(descriptor) - - # Then Expect - assert list(easy_list) == [descriptor] - assert easy_list[descriptor.unique_name] is descriptor diff --git a/tests/unit/variable/test_descriptor_bool.py b/tests/unit/variable/test_descriptor_bool.py index 3c181f0e..48372485 100644 --- a/tests/unit/variable/test_descriptor_bool.py +++ b/tests/unit/variable/test_descriptor_bool.py @@ -16,7 +16,6 @@ def descriptor(self): description='description', url='url', display_name='display_name', - parent=None, ) return descriptor @@ -45,7 +44,6 @@ def test_init_bool_value_type_exception(self, bool_value): description='description', url='url', display_name='display_name', - parent=None, ) def test_value(self, descriptor: DescriptorBool): diff --git a/tests/unit/variable/test_descriptor_number.py b/tests/unit/variable/test_descriptor_number.py index 6abd3029..3d3f7331 100644 --- a/tests/unit/variable/test_descriptor_number.py +++ b/tests/unit/variable/test_descriptor_number.py @@ -20,7 +20,6 @@ def descriptor(self): description='description', url='url', display_name='display_name', - parent=None, ) return descriptor @@ -51,7 +50,6 @@ def test_init_sc_unit(self): description='description', url='url', display_name='display_name', - parent=None, ) # Expect @@ -70,7 +68,6 @@ def test_init_sc_unit_unknown(self): description='description', url='url', display_name='display_name', - parent=None, ) @pytest.mark.parametrize('value', [True, 'string']) @@ -87,7 +84,6 @@ def test_init_value_type_exception(self, value): description='description', url='url', display_name='display_name', - parent=None, ) def test_init_variance_exception(self): @@ -104,7 +100,6 @@ def test_init_variance_exception(self): description='description', url='url', display_name='display_name', - parent=None, ) # test from_scipp diff --git a/tests/unit/variable/test_descriptor_str.py b/tests/unit/variable/test_descriptor_str.py index 46acc847..36b721c1 100644 --- a/tests/unit/variable/test_descriptor_str.py +++ b/tests/unit/variable/test_descriptor_str.py @@ -16,7 +16,6 @@ def descriptor(self): description='description', url='url', display_name='display_name', - parent=None, ) return descriptor @@ -44,7 +43,6 @@ def test_init_string_type_exception(self, string): description='description', url='url', display_name='display_name', - parent=None, ) def test_value(self, descriptor: DescriptorStr): diff --git a/tests/unit/variable/test_parameter.py b/tests/unit/variable/test_parameter.py index 1ac05b45..5376edf3 100644 --- a/tests/unit/variable/test_parameter.py +++ b/tests/unit/variable/test_parameter.py @@ -29,7 +29,6 @@ def parameter(self) -> Parameter: url='url', display_name='display_name', callback=self.mock_callback, - parent=None, ) return parameter @@ -96,7 +95,6 @@ def test_init_value_min_exception(self): url='url', display_name='display_name', callback=mock_callback, - parent=None, ) def test_init_value_max_exception(self): @@ -117,7 +115,6 @@ def test_init_value_max_exception(self): url='url', display_name='display_name', callback=mock_callback, - parent=None, ) def test_make_dependent_on(self, normal_parameter: Parameter): diff --git a/tests/unit/variable/test_parameter_dependency_serialization.py b/tests/unit/variable/test_parameter_dependency_serialization.py index 0dddc5d5..98daed45 100644 --- a/tests/unit/variable/test_parameter_dependency_serialization.py +++ b/tests/unit/variable/test_parameter_dependency_serialization.py @@ -40,7 +40,7 @@ def test_independent_parameter_serialization(self, clear_global_map): param = Parameter(name='test', value=5.0, unit='m', min=0, max=10) # Serialize - serialized = param.as_dict() + serialized = param.to_dict() # Should not contain dependency fields assert '_dependency_string' not in serialized @@ -68,7 +68,7 @@ def test_dependent_parameter_serialization(self, clear_global_map): ) # Serialize dependent parameter - serialized = b.as_dict() + serialized = b.to_dict() # Should contain dependency information assert serialized['_dependency_string'] == '2 * a' @@ -106,7 +106,7 @@ def test_dependency_resolution_after_deserialization(self, clear_global_map): assert c.value == 5.0 # 2 + 3 # Serialize all parameters - params_data = {'a': a.as_dict(), 'b': b.as_dict(), 'c': c.as_dict()} + params_data = {'a': a.to_dict(), 'b': b.to_dict(), 'c': c.to_dict()} # Clear and deserialize (manual approach) global_object.map._clear() @@ -151,7 +151,7 @@ def test_dependency_resolution_after_deserialization_desired_unit(self, clear_gl assert c.unit == 'cm' # Serialize all parameters - params_data = {'a': a.as_dict(), 'b': b.as_dict(), 'c': c.as_dict()} + params_data = {'a': a.to_dict(), 'b': b.to_dict(), 'c': c.to_dict()} # Clear and deserialize (manual approach) global_object.map._clear() @@ -192,8 +192,8 @@ def test_unique_name_dependency_serialization(self, clear_global_map): ) # Serialize both parameters - a_serialized = a.as_dict() - b_serialized = b.as_dict() + a_serialized = a.to_dict() + b_serialized = b.to_dict() # Should contain unique name mapping assert b_serialized['_dependency_string'] == '2 * __Parameter_0__' @@ -237,9 +237,9 @@ def test_json_serialization_roundtrip(self, clear_global_map): # Serialize to JSON params_data = { - 'length': length.as_dict(), - 'width': width.as_dict(), - 'area': area.as_dict(), + 'length': length.to_dict(), + 'width': width.to_dict(), + 'area': area.to_dict(), } json_str = json.dumps(params_data, default=str) @@ -281,7 +281,7 @@ def test_multiple_dependent_parameters(self, clear_global_map): assert z.value == 6.0 # 4 + 2 # Serialize all - params_data = {'x': x.as_dict(), 'y': y.as_dict(), 'z': z.as_dict()} + params_data = {'x': x.to_dict(), 'y': y.to_dict(), 'z': z.to_dict()} # Deserialize and resolve global_object.map._clear() @@ -319,7 +319,7 @@ def test_dependency_with_descriptor_number(self, clear_global_map): # Then # Serialize all - params_data = {'x': x.as_dict(), 'y': y.as_dict(), 'z': z.as_dict()} + params_data = {'x': x.to_dict(), 'y': y.to_dict(), 'z': z.to_dict()} # Deserialize and resolve global_object.map._clear() new_params = {} @@ -350,7 +350,7 @@ def test_get_parameters_with_pending_dependencies(self, clear_global_map): ) # Serialize and deserialize - params_data = {'a': a.as_dict(), 'b': b.as_dict()} + params_data = {'a': a.to_dict(), 'b': b.to_dict()} global_object.map._clear() new_params = {} for name, data in params_data.items(): @@ -376,7 +376,7 @@ def test_error_handling_missing_dependency(self, clear_global_map): ) # Serialize b but not a - b_data = b.as_dict() + b_data = b.to_dict() # Deserialize without a in the global map global_object.map._clear() @@ -440,7 +440,7 @@ def test_serializer_id_system_order_independence(self, clear_global_map, order): y_dep_id = y._DescriptorNumber__serializer_id # Serialize all parameters - params_data = {'x': x.as_dict(), 'y': y.as_dict(), 'z': z.as_dict()} + params_data = {'x': x.to_dict(), 'y': y.to_dict(), 'z': z.to_dict()} # Verify dependency IDs are in serialized data assert params_data['x']['__serializer_id'] == x_dep_id @@ -492,7 +492,7 @@ def test_deserialize_and_resolve_parameters_helper(self, clear_global_map): assert c.value == 5.0 # 2 + 3 # Serialize all parameters - params_data = {'a': a.as_dict(), 'b': b.as_dict(), 'c': c.as_dict()} + params_data = {'a': a.to_dict(), 'b': b.to_dict(), 'c': c.to_dict()} # Clear global map global_object.map._clear() From 03f86be3eeb2b926458883a484ce1b210e377a31 Mon Sep 17 00:00:00 2001 From: rozyczko Date: Mon, 14 Sep 2026 09:32:26 +0200 Subject: [PATCH 5/5] fix the behaviour when parameters are passed --- src/easyscience/fitting/multi_fitter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/easyscience/fitting/multi_fitter.py b/src/easyscience/fitting/multi_fitter.py index b8b89c3a..55933d4e 100644 --- a/src/easyscience/fitting/multi_fitter.py +++ b/src/easyscience/fitting/multi_fitter.py @@ -39,7 +39,7 @@ def __init__( # Aggregate the fit objects so a single object can be sent to Fitter. # *-unpacking keeps any sequence (list, tuple, etc) working, as the # old CollectionBase container did. - self._fit_objects = EasyList(*fit_objects) + self._fit_objects = EasyList(*fit_objects, protected_types=ModelBase) self._fit_functions = list(fit_functions) # Initialize with the first of the fit_functions, without this it is # not possible to change the fitting engine. With no functions given