easyscience 1.3.0__tar.gz → 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {easyscience-1.3.0 → easyscience-2.0.0}/LICENSE +1 -1
- {easyscience-1.3.0 → easyscience-2.0.0}/PKG-INFO +3 -6
- {easyscience-1.3.0 → easyscience-2.0.0}/pyproject.toml +3 -6
- easyscience-2.0.0/src/easyscience/__init__.py +24 -0
- easyscience-2.0.0/src/easyscience/__version__.py +1 -0
- easyscience-2.0.0/src/easyscience/base_classes/__init__.py +9 -0
- easyscience-2.0.0/src/easyscience/base_classes/based_base.py +198 -0
- easyscience-1.3.0/src/easyscience/Objects/Groups.py → easyscience-2.0.0/src/easyscience/base_classes/collection_base.py +17 -21
- easyscience-2.0.0/src/easyscience/base_classes/obj_base.py +162 -0
- easyscience-2.0.0/src/easyscience/fitting/calculators/__init__.py +7 -0
- easyscience-1.3.0/src/easyscience/Objects/Inferface.py → easyscience-2.0.0/src/easyscience/fitting/calculators/interface_factory.py +12 -21
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/fitting/fitter.py +3 -19
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/fitting/minimizers/__init__.py +2 -2
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/fitting/minimizers/minimizer_base.py +9 -30
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/fitting/minimizers/minimizer_bumps.py +31 -20
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/fitting/minimizers/minimizer_dfo.py +23 -12
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/fitting/minimizers/minimizer_lmfit.py +25 -14
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/fitting/multi_fitter.py +4 -5
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/global_object/global_object.py +3 -7
- easyscience-2.0.0/src/easyscience/global_object/hugger/__init__.py +4 -0
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/global_object/hugger/hugger.py +3 -6
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/global_object/hugger/property.py +4 -7
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/global_object/logger.py +2 -5
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/global_object/map.py +2 -5
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/global_object/undo_redo.py +5 -8
- easyscience-2.0.0/src/easyscience/io/__init__.py +12 -0
- easyscience-1.3.0/src/easyscience/Utils/io/template.py → easyscience-2.0.0/src/easyscience/io/serializer_base.py +34 -55
- easyscience-2.0.0/src/easyscience/io/serializer_component.py +80 -0
- easyscience-2.0.0/src/easyscience/io/serializer_dict.py +54 -0
- {easyscience-1.3.0/src/easyscience/Objects → easyscience-2.0.0/src/easyscience}/job/analysis.py +9 -11
- {easyscience-1.3.0/src/easyscience/Objects → easyscience-2.0.0/src/easyscience}/job/experiment.py +4 -4
- {easyscience-1.3.0/src/easyscience/Objects → easyscience-2.0.0/src/easyscience}/job/job.py +7 -7
- {easyscience-1.3.0/src/easyscience/Objects → easyscience-2.0.0/src/easyscience}/job/theoreticalmodel.py +4 -4
- {easyscience-1.3.0/src/easyscience/Utils/io → easyscience-2.0.0/src/easyscience/legacy}/dict.py +9 -9
- {easyscience-1.3.0/src/easyscience/Utils/io → easyscience-2.0.0/src/easyscience/legacy}/json.py +8 -60
- easyscience-1.3.0/src/easyscience/Objects/core.py → easyscience-2.0.0/src/easyscience/legacy/legacy_core.py +9 -12
- {easyscience-1.3.0/src/easyscience/Utils/io → easyscience-2.0.0/src/easyscience/legacy}/xml.py +8 -11
- easyscience-2.0.0/src/easyscience/models/__init__.py +3 -0
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/models/polynomial.py +11 -47
- easyscience-2.0.0/src/easyscience/utils/__init__.py +3 -0
- {easyscience-1.3.0/src/easyscience/Utils → easyscience-2.0.0/src/easyscience/utils}/classTools.py +8 -11
- {easyscience-1.3.0/src/easyscience/Utils → easyscience-2.0.0/src/easyscience/utils}/classUtils.py +2 -5
- {easyscience-1.3.0/src/easyscience/Utils → easyscience-2.0.0/src/easyscience/utils}/decorators.py +2 -5
- {easyscience-1.3.0/src/easyscience/Utils → easyscience-2.0.0/src/easyscience/utils}/string.py +2 -6
- {easyscience-1.3.0/src/easyscience/Objects → easyscience-2.0.0/src/easyscience}/variable/descriptor_any_type.py +2 -2
- {easyscience-1.3.0/src/easyscience/Objects → easyscience-2.0.0/src/easyscience}/variable/descriptor_array.py +4 -4
- {easyscience-1.3.0/src/easyscience/Objects → easyscience-2.0.0/src/easyscience}/variable/descriptor_base.py +7 -7
- {easyscience-1.3.0/src/easyscience/Objects → easyscience-2.0.0/src/easyscience}/variable/descriptor_bool.py +2 -2
- {easyscience-1.3.0/src/easyscience/Objects → easyscience-2.0.0/src/easyscience}/variable/descriptor_number.py +84 -15
- {easyscience-1.3.0/src/easyscience/Objects → easyscience-2.0.0/src/easyscience}/variable/descriptor_str.py +2 -2
- {easyscience-1.3.0/src/easyscience/Objects → easyscience-2.0.0/src/easyscience}/variable/parameter.py +329 -193
- easyscience-1.3.0/src/easyscience/Constraints.py +0 -498
- easyscience-1.3.0/src/easyscience/Datasets/__init__.py +0 -6
- easyscience-1.3.0/src/easyscience/Datasets/xarray.py +0 -823
- easyscience-1.3.0/src/easyscience/Objects/ObjectClasses.py +0 -367
- easyscience-1.3.0/src/easyscience/Objects/__init__.py +0 -6
- easyscience-1.3.0/src/easyscience/REDIRECT.py +0 -9
- easyscience-1.3.0/src/easyscience/Utils/Exceptions.py +0 -15
- easyscience-1.3.0/src/easyscience/Utils/__init__.py +0 -6
- easyscience-1.3.0/src/easyscience/Utils/io/__init__.py +0 -6
- easyscience-1.3.0/src/easyscience/__init__.py +0 -31
- easyscience-1.3.0/src/easyscience/__version__.py +0 -1
- easyscience-1.3.0/src/easyscience/global_object/hugger/__init__.py +0 -6
- easyscience-1.3.0/src/easyscience/models/__init__.py +0 -6
- {easyscience-1.3.0 → easyscience-2.0.0}/.gitignore +0 -0
- {easyscience-1.3.0 → easyscience-2.0.0}/README.md +0 -0
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/fitting/__init__.py +0 -0
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/fitting/available_minimizers.py +0 -0
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/fitting/minimizers/factory.py +0 -0
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/fitting/minimizers/utils.py +0 -0
- {easyscience-1.3.0 → easyscience-2.0.0}/src/easyscience/global_object/__init__.py +0 -0
- {easyscience-1.3.0/src/easyscience/Objects → easyscience-2.0.0/src/easyscience}/job/__init__.py +0 -0
- {easyscience-1.3.0/src/easyscience/Objects → easyscience-2.0.0/src/easyscience}/variable/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
BSD 3-Clause License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2025, Easyscience contributors (https://github.com/EasyScience)
|
|
4
4
|
All rights reserved.
|
|
5
5
|
|
|
6
6
|
Redistribution and use in source and binary forms, with or without
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: easyscience
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: Generic logic for easyScience libraries
|
|
5
5
|
Project-URL: homepage, https://github.com/EasyScience/EasyScience
|
|
6
6
|
Project-URL: documentation, https://easyscience.github.io/EasyScience/
|
|
7
7
|
Author: EasyScience contributors
|
|
8
8
|
License: BSD 3-Clause License
|
|
9
9
|
|
|
10
|
-
Copyright (c)
|
|
10
|
+
Copyright (c) 2025, Easyscience contributors (https://github.com/EasyScience)
|
|
11
11
|
All rights reserved.
|
|
12
12
|
|
|
13
13
|
Redistribution and use in source and binary forms, with or without
|
|
@@ -40,21 +40,18 @@ Classifier: License :: OSI Approved :: BSD License
|
|
|
40
40
|
Classifier: Operating System :: OS Independent
|
|
41
41
|
Classifier: Programming Language :: Python :: 3
|
|
42
42
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
43
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
44
43
|
Classifier: Programming Language :: Python :: 3.11
|
|
45
44
|
Classifier: Programming Language :: Python :: 3.12
|
|
46
45
|
Classifier: Programming Language :: Python :: 3.13
|
|
47
46
|
Classifier: Topic :: Scientific/Engineering
|
|
48
|
-
Requires-Python: >=3.
|
|
47
|
+
Requires-Python: >=3.11
|
|
49
48
|
Requires-Dist: asteval
|
|
50
49
|
Requires-Dist: bumps
|
|
51
50
|
Requires-Dist: dfo-ls
|
|
52
51
|
Requires-Dist: lmfit
|
|
53
52
|
Requires-Dist: numpy
|
|
54
|
-
Requires-Dist: pint
|
|
55
53
|
Requires-Dist: scipp
|
|
56
54
|
Requires-Dist: uncertainties
|
|
57
|
-
Requires-Dist: xarray
|
|
58
55
|
Provides-Extra: dev
|
|
59
56
|
Requires-Dist: build; extra == 'dev'
|
|
60
57
|
Requires-Dist: codecov; extra == 'dev'
|
|
@@ -21,13 +21,12 @@ classifiers = [
|
|
|
21
21
|
"Topic :: Scientific/Engineering",
|
|
22
22
|
"Programming Language :: Python :: 3",
|
|
23
23
|
"Programming Language :: Python :: 3 :: Only",
|
|
24
|
-
"Programming Language :: Python :: 3.10",
|
|
25
24
|
"Programming Language :: Python :: 3.11",
|
|
26
25
|
"Programming Language :: Python :: 3.12",
|
|
27
26
|
"Programming Language :: Python :: 3.13",
|
|
28
27
|
"Development Status :: 3 - Alpha"
|
|
29
28
|
]
|
|
30
|
-
requires-python = ">=3.
|
|
29
|
+
requires-python = ">=3.11"
|
|
31
30
|
dependencies = [
|
|
32
31
|
"asteval",
|
|
33
32
|
"bumps",
|
|
@@ -35,8 +34,6 @@ dependencies = [
|
|
|
35
34
|
"lmfit",
|
|
36
35
|
"numpy",
|
|
37
36
|
"uncertainties",
|
|
38
|
-
"xarray",
|
|
39
|
-
"pint", # Only to ensure that unit is reported as dimensionless rather than empty string
|
|
40
37
|
"scipp"
|
|
41
38
|
]
|
|
42
39
|
|
|
@@ -75,6 +72,7 @@ packages = ["src"]
|
|
|
75
72
|
|
|
76
73
|
[tool.hatch.build.targets.wheel]
|
|
77
74
|
packages = ["src/easyscience"]
|
|
75
|
+
exclude = ["src/easyscience/legacy"]
|
|
78
76
|
|
|
79
77
|
[tool.coverage.run]
|
|
80
78
|
source = ["src/easyscience"]
|
|
@@ -128,10 +126,9 @@ force-single-line = true
|
|
|
128
126
|
legacy_tox_ini = """
|
|
129
127
|
[tox]
|
|
130
128
|
isolated_build = True
|
|
131
|
-
envlist = py{3.
|
|
129
|
+
envlist = py{3.11,3.12,3.13}
|
|
132
130
|
[gh-actions]
|
|
133
131
|
python =
|
|
134
|
-
3.10: py310
|
|
135
132
|
3.11: py311
|
|
136
133
|
3.12: py312
|
|
137
134
|
3.13: py313
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from .global_object import GlobalObject
|
|
2
|
+
|
|
3
|
+
# Must be executed before any other imports
|
|
4
|
+
global_object = GlobalObject()
|
|
5
|
+
global_object.instantiate_stack()
|
|
6
|
+
global_object.stack.enabled = False
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
from .__version__ import __version__ as __version__ # noqa: E402
|
|
10
|
+
from .base_classes import ObjBase # noqa: E402
|
|
11
|
+
from .fitting import AvailableMinimizers # noqa: E402
|
|
12
|
+
from .fitting import Fitter # noqa: E402
|
|
13
|
+
from .variable import DescriptorNumber # noqa: E402
|
|
14
|
+
from .variable import Parameter # noqa: E402
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
__version__,
|
|
18
|
+
global_object,
|
|
19
|
+
ObjBase,
|
|
20
|
+
AvailableMinimizers,
|
|
21
|
+
Fitter,
|
|
22
|
+
DescriptorNumber,
|
|
23
|
+
Parameter,
|
|
24
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.0.0'
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 EasyScience contributors <core@easyscience.software>
|
|
4
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
# © 2021-2025 Contributors to the EasyScience project <https://github.com/easyScience/EasyScience
|
|
6
|
+
from inspect import getfullargspec
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
from typing import Iterable
|
|
9
|
+
from typing import List
|
|
10
|
+
from typing import Optional
|
|
11
|
+
from typing import Set
|
|
12
|
+
|
|
13
|
+
from easyscience import global_object
|
|
14
|
+
|
|
15
|
+
from ..io import SerializerComponent
|
|
16
|
+
from ..variable import Parameter
|
|
17
|
+
from ..variable.descriptor_base import DescriptorBase
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from ..fitting.calculators import InterfaceFactoryTemplate
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class BasedBase(SerializerComponent):
|
|
24
|
+
__slots__ = ['_name', '_global_object', 'user_data', '_kwargs']
|
|
25
|
+
|
|
26
|
+
_REDIRECT = {}
|
|
27
|
+
|
|
28
|
+
def __init__(self, name: str, interface: Optional[InterfaceFactoryTemplate] = None, unique_name: Optional[str] = None):
|
|
29
|
+
self._global_object = global_object
|
|
30
|
+
if unique_name is None:
|
|
31
|
+
unique_name = self._global_object.generate_unique_name(self.__class__.__name__)
|
|
32
|
+
self._unique_name = unique_name
|
|
33
|
+
self._name = name
|
|
34
|
+
self._global_object.map.add_vertex(self, obj_type='created')
|
|
35
|
+
self.interface = interface
|
|
36
|
+
self.user_data: dict = {}
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def _arg_spec(self) -> Set[str]:
|
|
40
|
+
base_cls = getattr(self, '__old_class__', self.__class__)
|
|
41
|
+
spec = getfullargspec(base_cls.__init__)
|
|
42
|
+
names = set(spec.args[1:])
|
|
43
|
+
return names
|
|
44
|
+
|
|
45
|
+
def __reduce__(self):
|
|
46
|
+
"""
|
|
47
|
+
Make the class picklable.
|
|
48
|
+
Due to the nature of the dynamic class definitions special measures need to be taken.
|
|
49
|
+
|
|
50
|
+
:return: Tuple consisting of how to make the object
|
|
51
|
+
:rtype: tuple
|
|
52
|
+
"""
|
|
53
|
+
state = self.encode()
|
|
54
|
+
cls = getattr(self, '__old_class__', self.__class__)
|
|
55
|
+
return cls.from_dict, (state,)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def unique_name(self) -> str:
|
|
59
|
+
"""Get the unique name of the object."""
|
|
60
|
+
return self._unique_name
|
|
61
|
+
|
|
62
|
+
@unique_name.setter
|
|
63
|
+
def unique_name(self, new_unique_name: str):
|
|
64
|
+
"""Set a new unique name for the object. The old name is still kept in the map.
|
|
65
|
+
|
|
66
|
+
:param new_unique_name: New unique name for the object"""
|
|
67
|
+
if not isinstance(new_unique_name, str):
|
|
68
|
+
raise TypeError('Unique name has to be a string.')
|
|
69
|
+
self._unique_name = new_unique_name
|
|
70
|
+
self._global_object.map.add_vertex(self)
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def name(self) -> str:
|
|
74
|
+
"""
|
|
75
|
+
Get the common name of the object.
|
|
76
|
+
|
|
77
|
+
:return: Common name of the object
|
|
78
|
+
"""
|
|
79
|
+
return self._name
|
|
80
|
+
|
|
81
|
+
@name.setter
|
|
82
|
+
def name(self, new_name: str):
|
|
83
|
+
"""
|
|
84
|
+
Set a new common name for the object.
|
|
85
|
+
|
|
86
|
+
:param new_name: New name for the object
|
|
87
|
+
:return: None
|
|
88
|
+
"""
|
|
89
|
+
self._name = new_name
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def interface(self) -> InterfaceFactoryTemplate:
|
|
93
|
+
"""
|
|
94
|
+
Get the current interface of the object
|
|
95
|
+
"""
|
|
96
|
+
return self._interface
|
|
97
|
+
|
|
98
|
+
@interface.setter
|
|
99
|
+
def interface(self, new_interface: InterfaceFactoryTemplate):
|
|
100
|
+
"""
|
|
101
|
+
Set the current interface to the object and generate bindings if possible. iF.e.
|
|
102
|
+
```
|
|
103
|
+
def __init__(self, bar, interface=None, **kwargs):
|
|
104
|
+
super().__init__(self, **kwargs)
|
|
105
|
+
self.foo = bar
|
|
106
|
+
self.interface = interface # As final step after initialization to set correct bindings.
|
|
107
|
+
```
|
|
108
|
+
"""
|
|
109
|
+
self._interface = new_interface
|
|
110
|
+
if new_interface is not None:
|
|
111
|
+
self.generate_bindings()
|
|
112
|
+
|
|
113
|
+
def generate_bindings(self):
|
|
114
|
+
"""
|
|
115
|
+
Generate or re-generate bindings to an interface (if exists)
|
|
116
|
+
|
|
117
|
+
:raises: AttributeError
|
|
118
|
+
"""
|
|
119
|
+
if self.interface is None:
|
|
120
|
+
raise AttributeError('Interface error for generating bindings. `interface` has to be set.')
|
|
121
|
+
interfaceable_children = [
|
|
122
|
+
key
|
|
123
|
+
for key in self._global_object.map.get_edges(self)
|
|
124
|
+
if issubclass(type(self._global_object.map.get_item_by_key(key)), BasedBase)
|
|
125
|
+
]
|
|
126
|
+
for child_key in interfaceable_children:
|
|
127
|
+
child = self._global_object.map.get_item_by_key(child_key)
|
|
128
|
+
child.interface = self.interface
|
|
129
|
+
self.interface.generate_bindings(self)
|
|
130
|
+
|
|
131
|
+
def switch_interface(self, new_interface_name: str):
|
|
132
|
+
"""
|
|
133
|
+
Switch or create a new interface.
|
|
134
|
+
"""
|
|
135
|
+
if self.interface is None:
|
|
136
|
+
raise AttributeError('Interface error for generating bindings. `interface` has to be set.')
|
|
137
|
+
self.interface.switch(new_interface_name)
|
|
138
|
+
self.generate_bindings()
|
|
139
|
+
|
|
140
|
+
def get_parameters(self) -> List[Parameter]:
|
|
141
|
+
"""
|
|
142
|
+
Get all parameter objects as a list.
|
|
143
|
+
|
|
144
|
+
:return: List of `Parameter` objects.
|
|
145
|
+
"""
|
|
146
|
+
par_list = []
|
|
147
|
+
for key, item in self._kwargs.items():
|
|
148
|
+
if hasattr(item, 'get_parameters'):
|
|
149
|
+
par_list = [*par_list, *item.get_parameters()]
|
|
150
|
+
elif isinstance(item, Parameter):
|
|
151
|
+
par_list.append(item)
|
|
152
|
+
return par_list
|
|
153
|
+
|
|
154
|
+
def _get_linkable_attributes(self) -> List[DescriptorBase]:
|
|
155
|
+
"""
|
|
156
|
+
Get all objects which can be linked against as a list.
|
|
157
|
+
|
|
158
|
+
:return: List of `Descriptor`/`Parameter` objects.
|
|
159
|
+
"""
|
|
160
|
+
item_list = []
|
|
161
|
+
for key, item in self._kwargs.items():
|
|
162
|
+
if hasattr(item, '_get_linkable_attributes'):
|
|
163
|
+
item_list = [*item_list, *item._get_linkable_attributes()]
|
|
164
|
+
elif issubclass(type(item), (DescriptorBase)):
|
|
165
|
+
item_list.append(item)
|
|
166
|
+
return item_list
|
|
167
|
+
|
|
168
|
+
def get_fit_parameters(self) -> List[Parameter]:
|
|
169
|
+
"""
|
|
170
|
+
Get all objects which can be fitted (and are not fixed) as a list.
|
|
171
|
+
|
|
172
|
+
:return: List of `Parameter` objects which can be used in fitting.
|
|
173
|
+
"""
|
|
174
|
+
fit_list = []
|
|
175
|
+
for key, item in self._kwargs.items():
|
|
176
|
+
if hasattr(item, 'get_fit_parameters'):
|
|
177
|
+
fit_list = [*fit_list, *item.get_fit_parameters()]
|
|
178
|
+
elif isinstance(item, Parameter):
|
|
179
|
+
if item.independent and not item.fixed:
|
|
180
|
+
fit_list.append(item)
|
|
181
|
+
return fit_list
|
|
182
|
+
|
|
183
|
+
def __dir__(self) -> Iterable[str]:
|
|
184
|
+
"""
|
|
185
|
+
This creates auto-completion and helps out in iPython notebooks.
|
|
186
|
+
|
|
187
|
+
:return: list of function and parameter names for auto-completion
|
|
188
|
+
"""
|
|
189
|
+
new_class_objs = list(k for k in dir(self.__class__) if not k.startswith('_'))
|
|
190
|
+
return sorted(new_class_objs)
|
|
191
|
+
|
|
192
|
+
def __copy__(self) -> BasedBase:
|
|
193
|
+
"""Return a copy of the object."""
|
|
194
|
+
temp = self.as_dict(skip=['unique_name'])
|
|
195
|
+
new_obj = self.__class__.from_dict(temp)
|
|
196
|
+
return new_obj
|
|
197
|
+
|
|
198
|
+
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText:
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 EasyScience contributors <core@easyscience.software>
|
|
2
2
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
3
|
-
# © 2021-
|
|
3
|
+
# © 2021-2025 Contributors to the EasyScience project <https://github.com/easyScience/EasyScience
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
-
__author__ = 'github.com/wardsimon'
|
|
8
|
-
__version__ = '0.1.0'
|
|
9
|
-
|
|
10
7
|
from collections.abc import MutableSequence
|
|
11
8
|
from numbers import Number
|
|
12
9
|
from typing import TYPE_CHECKING
|
|
@@ -15,32 +12,31 @@ from typing import Callable
|
|
|
15
12
|
from typing import List
|
|
16
13
|
from typing import Optional
|
|
17
14
|
from typing import Tuple
|
|
18
|
-
from typing import TypeVar
|
|
19
15
|
from typing import Union
|
|
20
16
|
|
|
21
17
|
from easyscience.global_object.undo_redo import NotarizedDict
|
|
22
|
-
|
|
23
|
-
from
|
|
18
|
+
|
|
19
|
+
from ..variable.descriptor_base import DescriptorBase
|
|
20
|
+
from .based_base import BasedBase
|
|
24
21
|
|
|
25
22
|
if TYPE_CHECKING:
|
|
26
|
-
from
|
|
27
|
-
|
|
28
|
-
V = TypeVar('V', bound=DescriptorBase)
|
|
23
|
+
from ..fitting.calculators import InterfaceFactoryTemplate
|
|
24
|
+
|
|
29
25
|
|
|
30
26
|
|
|
31
|
-
class
|
|
27
|
+
class CollectionBase(BasedBase, MutableSequence):
|
|
32
28
|
"""
|
|
33
29
|
This is the base class for which all higher level classes are built off of.
|
|
34
30
|
NOTE: This object is serializable only if parameters are supplied as:
|
|
35
|
-
`
|
|
36
|
-
cheat with `
|
|
31
|
+
`ObjBase(a=value, b=value)`. For `Parameter` or `Descriptor` objects we can
|
|
32
|
+
cheat with `ObjBase(*[Descriptor(...), Parameter(...), ...])`.
|
|
37
33
|
"""
|
|
38
34
|
|
|
39
35
|
def __init__(
|
|
40
36
|
self,
|
|
41
37
|
name: str,
|
|
42
|
-
*args: Union[
|
|
43
|
-
interface: Optional[
|
|
38
|
+
*args: Union[BasedBase, DescriptorBase],
|
|
39
|
+
interface: Optional[InterfaceFactoryTemplate] = None,
|
|
44
40
|
unique_name: Optional[str] = None,
|
|
45
41
|
**kwargs,
|
|
46
42
|
):
|
|
@@ -95,7 +91,7 @@ class BaseCollection(BasedBase, MutableSequence):
|
|
|
95
91
|
self.interface = interface
|
|
96
92
|
self._kwargs._stack_enabled = True
|
|
97
93
|
|
|
98
|
-
def insert(self, index: int, value: Union[
|
|
94
|
+
def insert(self, index: int, value: Union[DescriptorBase, BasedBase]) -> None:
|
|
99
95
|
"""
|
|
100
96
|
Insert an object into the collection at an index.
|
|
101
97
|
|
|
@@ -122,14 +118,14 @@ class BaseCollection(BasedBase, MutableSequence):
|
|
|
122
118
|
else:
|
|
123
119
|
raise AttributeError('Only EasyScience objects can be put into an EasyScience group')
|
|
124
120
|
|
|
125
|
-
def __getitem__(self, idx: Union[int, slice]) -> Union[
|
|
121
|
+
def __getitem__(self, idx: Union[int, slice]) -> Union[DescriptorBase, BasedBase]:
|
|
126
122
|
"""
|
|
127
123
|
Get an item in the collection based on its index.
|
|
128
124
|
|
|
129
125
|
:param idx: index or slice of the collection.
|
|
130
126
|
:type idx: Union[int, slice]
|
|
131
127
|
:return: Object at index `idx`
|
|
132
|
-
:rtype: Union[Parameter, Descriptor,
|
|
128
|
+
:rtype: Union[Parameter, Descriptor, ObjBase, 'CollectionBase']
|
|
133
129
|
"""
|
|
134
130
|
if isinstance(idx, slice):
|
|
135
131
|
start, stop, step = idx.indices(len(self))
|
|
@@ -156,7 +152,7 @@ class BaseCollection(BasedBase, MutableSequence):
|
|
|
156
152
|
keys = list(self._kwargs.keys())
|
|
157
153
|
return self._kwargs[keys[idx]]
|
|
158
154
|
|
|
159
|
-
def __setitem__(self, key: int, value: Union[
|
|
155
|
+
def __setitem__(self, key: int, value: Union[BasedBase, DescriptorBase]) -> None:
|
|
160
156
|
"""
|
|
161
157
|
Set an item via it's index.
|
|
162
158
|
|
|
@@ -238,7 +234,7 @@ class BaseCollection(BasedBase, MutableSequence):
|
|
|
238
234
|
def __repr__(self) -> str:
|
|
239
235
|
return f"{self.__class__.__name__} `{getattr(self, 'name')}` of length {len(self)}"
|
|
240
236
|
|
|
241
|
-
def sort(self, mapping: Callable[[Union[
|
|
237
|
+
def sort(self, mapping: Callable[[Union[BasedBase, DescriptorBase]], Any], reverse: bool = False) -> None:
|
|
242
238
|
"""
|
|
243
239
|
Sort the collection according to the given mapping.
|
|
244
240
|
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 EasyScience contributors <core@easyscience.software>
|
|
4
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
# © 2021-2025 Contributors to the EasyScience project <https://github.com/easyScience/EasyScience
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
from typing import Callable
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from ..utils.classTools import addLoggedProp
|
|
11
|
+
from ..variable.descriptor_base import DescriptorBase
|
|
12
|
+
from .based_base import BasedBase
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from ..io import SerializerComponent
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ObjBase(BasedBase):
|
|
20
|
+
"""
|
|
21
|
+
This is the base class for which all higher level classes are built off of.
|
|
22
|
+
NOTE: This object is serializable only if parameters are supplied as:
|
|
23
|
+
`ObjBase(a=value, b=value)`. For `Parameter` or `Descriptor` objects we can
|
|
24
|
+
cheat with `ObjBase(*[Descriptor(...), Parameter(...), ...])`.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
name: str,
|
|
30
|
+
unique_name: Optional[str] = None,
|
|
31
|
+
*args: Optional[SerializerComponent],
|
|
32
|
+
**kwargs: Optional[SerializerComponent],
|
|
33
|
+
):
|
|
34
|
+
"""
|
|
35
|
+
Set up the base class.
|
|
36
|
+
|
|
37
|
+
:param name: Name of this object
|
|
38
|
+
:param args: Any arguments?
|
|
39
|
+
:param kwargs: Fields which this class should contain
|
|
40
|
+
"""
|
|
41
|
+
super(ObjBase, self).__init__(name=name, unique_name=unique_name)
|
|
42
|
+
# If Parameter or Descriptor is given as arguments...
|
|
43
|
+
for arg in args:
|
|
44
|
+
if issubclass(type(arg), (ObjBase, DescriptorBase)):
|
|
45
|
+
kwargs[getattr(arg, 'name')] = arg
|
|
46
|
+
# Set kwargs, also useful for serialization
|
|
47
|
+
known_keys = self.__dict__.keys()
|
|
48
|
+
self._kwargs = kwargs
|
|
49
|
+
for key in kwargs.keys():
|
|
50
|
+
if key in known_keys:
|
|
51
|
+
raise AttributeError('Kwargs cannot overwrite class attributes in ObjBase.')
|
|
52
|
+
if issubclass(type(kwargs[key]), (BasedBase, DescriptorBase)) or 'CollectionBase' in [
|
|
53
|
+
c.__name__ for c in type(kwargs[key]).__bases__
|
|
54
|
+
]:
|
|
55
|
+
self._global_object.map.add_edge(self, kwargs[key])
|
|
56
|
+
self._global_object.map.reset_type(kwargs[key], 'created_internal')
|
|
57
|
+
addLoggedProp(
|
|
58
|
+
self,
|
|
59
|
+
key,
|
|
60
|
+
self.__getter(key),
|
|
61
|
+
self.__setter(key),
|
|
62
|
+
get_id=key,
|
|
63
|
+
my_self=self,
|
|
64
|
+
test_class=ObjBase,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def _add_component(self, key: str, component: SerializerComponent) -> None:
|
|
68
|
+
"""
|
|
69
|
+
Dynamically add a component to the class. This is an internal method, though can be called remotely.
|
|
70
|
+
The recommended alternative is to use typing, i.e.
|
|
71
|
+
|
|
72
|
+
class Foo(Bar):
|
|
73
|
+
def __init__(self, foo: Parameter, bar: Parameter):
|
|
74
|
+
super(Foo, self).__init__(bar=bar)
|
|
75
|
+
self._add_component("foo", foo)
|
|
76
|
+
|
|
77
|
+
Goes to:
|
|
78
|
+
class Foo(Bar):
|
|
79
|
+
foo: ClassVar[Parameter]
|
|
80
|
+
def __init__(self, foo: Parameter, bar: Parameter):
|
|
81
|
+
super(Foo, self).__init__(bar=bar)
|
|
82
|
+
self.foo = foo
|
|
83
|
+
|
|
84
|
+
:param key: Name of component to be added
|
|
85
|
+
:param component: Component to be added
|
|
86
|
+
:return: None
|
|
87
|
+
"""
|
|
88
|
+
self._kwargs[key] = component
|
|
89
|
+
self._global_object.map.add_edge(self, component)
|
|
90
|
+
self._global_object.map.reset_type(component, 'created_internal')
|
|
91
|
+
addLoggedProp(
|
|
92
|
+
self,
|
|
93
|
+
key,
|
|
94
|
+
self.__getter(key),
|
|
95
|
+
self.__setter(key),
|
|
96
|
+
get_id=key,
|
|
97
|
+
my_self=self,
|
|
98
|
+
test_class=ObjBase,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def __setattr__(self, key: str, value: SerializerComponent) -> None:
|
|
102
|
+
# Assume that the annotation is a ClassVar
|
|
103
|
+
old_obj = None
|
|
104
|
+
if (
|
|
105
|
+
hasattr(self.__class__, '__annotations__')
|
|
106
|
+
and key in self.__class__.__annotations__
|
|
107
|
+
and hasattr(self.__class__.__annotations__[key], '__args__')
|
|
108
|
+
and issubclass(
|
|
109
|
+
getattr(value, '__old_class__', value.__class__),
|
|
110
|
+
self.__class__.__annotations__[key].__args__,
|
|
111
|
+
)
|
|
112
|
+
):
|
|
113
|
+
if issubclass(type(getattr(self, key, None)), (BasedBase, DescriptorBase)):
|
|
114
|
+
old_obj = self.__getattribute__(key)
|
|
115
|
+
self._global_object.map.prune_vertex_from_edge(self, old_obj)
|
|
116
|
+
self._add_component(key, value)
|
|
117
|
+
else:
|
|
118
|
+
if hasattr(self, key) and issubclass(type(value), (BasedBase, DescriptorBase)):
|
|
119
|
+
old_obj = self.__getattribute__(key)
|
|
120
|
+
self._global_object.map.prune_vertex_from_edge(self, old_obj)
|
|
121
|
+
self._global_object.map.add_edge(self, value)
|
|
122
|
+
super(ObjBase, self).__setattr__(key, value)
|
|
123
|
+
# Update the interface bindings if something changed (BasedBase and Descriptor)
|
|
124
|
+
if old_obj is not None:
|
|
125
|
+
old_interface = getattr(self, 'interface', None)
|
|
126
|
+
if old_interface is not None:
|
|
127
|
+
self.generate_bindings()
|
|
128
|
+
|
|
129
|
+
def __repr__(self) -> str:
|
|
130
|
+
return f"{self.__class__.__name__} `{getattr(self, 'name')}`"
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
def __getter(key: str) -> Callable[[SerializerComponent], SerializerComponent]:
|
|
134
|
+
def getter(obj: SerializerComponent) -> SerializerComponent:
|
|
135
|
+
return obj._kwargs[key]
|
|
136
|
+
|
|
137
|
+
return getter
|
|
138
|
+
|
|
139
|
+
@staticmethod
|
|
140
|
+
def __setter(key: str) -> Callable[[SerializerComponent], None]:
|
|
141
|
+
def setter(obj: SerializerComponent, value: float) -> None:
|
|
142
|
+
if issubclass(obj._kwargs[key].__class__, (DescriptorBase)) and not issubclass(
|
|
143
|
+
value.__class__, (DescriptorBase)
|
|
144
|
+
):
|
|
145
|
+
obj._kwargs[key].value = value
|
|
146
|
+
else:
|
|
147
|
+
obj._kwargs[key] = value
|
|
148
|
+
|
|
149
|
+
return setter
|
|
150
|
+
|
|
151
|
+
# @staticmethod
|
|
152
|
+
# def __setter(key: str) -> Callable[[Union[B, V]], None]:
|
|
153
|
+
# def setter(obj: Union[V, B], value: float) -> None:
|
|
154
|
+
# if issubclass(obj._kwargs[key].__class__, Descriptor):
|
|
155
|
+
# if issubclass(obj._kwargs[key].__class__, Descriptor):
|
|
156
|
+
# obj._kwargs[key] = value
|
|
157
|
+
# else:
|
|
158
|
+
# obj._kwargs[key].value = value
|
|
159
|
+
# else:
|
|
160
|
+
# obj._kwargs[key] = value
|
|
161
|
+
#
|
|
162
|
+
# return setter
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 EasyScience contributors <core@easyscience.software>
|
|
2
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
3
|
+
# © 2021-2025 Contributors to the EasyScience project <https://github.com/easyScience/easyscience
|
|
4
|
+
|
|
5
|
+
from .interface_factory import InterfaceFactoryTemplate
|
|
6
|
+
|
|
7
|
+
__all__ = [InterfaceFactoryTemplate]
|
|
@@ -1,25 +1,19 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
# SPDX-FileCopyrightText:
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 EasyScience contributors <core@easyscience.software>
|
|
4
4
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
-
# © 2021-
|
|
6
|
-
|
|
7
|
-
__author__ = 'github.com/wardsimon'
|
|
8
|
-
__version__ = '0.1.0'
|
|
9
|
-
|
|
10
|
-
from abc import ABCMeta
|
|
5
|
+
# © 2021-2025 Contributors to the EasyScience project <https://github.com/easyScience/EasyScience
|
|
11
6
|
from typing import TYPE_CHECKING
|
|
12
7
|
from typing import Callable
|
|
13
8
|
from typing import List
|
|
14
9
|
from typing import NamedTuple
|
|
15
10
|
from typing import Optional
|
|
16
11
|
from typing import Type
|
|
17
|
-
from typing import TypeVar
|
|
18
12
|
|
|
19
|
-
_C = TypeVar('_C', bound=ABCMeta)
|
|
20
|
-
_M = TypeVar('_M')
|
|
21
13
|
if TYPE_CHECKING:
|
|
22
|
-
from
|
|
14
|
+
from abc import ABCMeta
|
|
15
|
+
|
|
16
|
+
from .. import Fitter
|
|
23
17
|
|
|
24
18
|
|
|
25
19
|
class InterfaceFactoryTemplate:
|
|
@@ -27,10 +21,10 @@ class InterfaceFactoryTemplate:
|
|
|
27
21
|
This class allows for the creation and transference of interfaces.
|
|
28
22
|
"""
|
|
29
23
|
|
|
30
|
-
def __init__(self, interface_list: List[
|
|
31
|
-
self._interfaces: List[
|
|
32
|
-
self._current_interface:
|
|
33
|
-
self.__interface_obj:
|
|
24
|
+
def __init__(self, interface_list: List[ABCMeta], *args, **kwargs):
|
|
25
|
+
self._interfaces: List[ABCMeta] = interface_list
|
|
26
|
+
self._current_interface: ABCMeta
|
|
27
|
+
self.__interface_obj: None = None
|
|
34
28
|
self.create(*args, **kwargs)
|
|
35
29
|
|
|
36
30
|
def create(self, *args, **kwargs):
|
|
@@ -60,7 +54,7 @@ class InterfaceFactoryTemplate:
|
|
|
60
54
|
def switch(self, new_interface: str, fitter: Optional[Type[Fitter]] = None):
|
|
61
55
|
"""
|
|
62
56
|
Changes the current interface to a new interface. The current interface is destroyed and
|
|
63
|
-
all
|
|
57
|
+
all SerializerComponent parameters carried over to the new interface. i.e. pick up where you left off.
|
|
64
58
|
|
|
65
59
|
:param new_interface: name of new interface to be created
|
|
66
60
|
:type new_interface: str
|
|
@@ -100,7 +94,7 @@ class InterfaceFactoryTemplate:
|
|
|
100
94
|
return [self.return_name(this_interface) for this_interface in self._interfaces]
|
|
101
95
|
|
|
102
96
|
@property
|
|
103
|
-
def current_interface(self) ->
|
|
97
|
+
def current_interface(self) -> ABCMeta:
|
|
104
98
|
"""
|
|
105
99
|
Returns the constructor for the currently selected interface.
|
|
106
100
|
|
|
@@ -174,7 +168,7 @@ class InterfaceFactoryTemplate:
|
|
|
174
168
|
prop._callback = item.make_prop(item_key)
|
|
175
169
|
prop._callback.fset(prop_value)
|
|
176
170
|
|
|
177
|
-
def __call__(self, *args, **kwargs) ->
|
|
171
|
+
def __call__(self, *args, **kwargs) -> None:
|
|
178
172
|
return self.__interface_obj
|
|
179
173
|
|
|
180
174
|
def __reduce__(self):
|
|
@@ -233,6 +227,3 @@ class ItemContainer(NamedTuple):
|
|
|
233
227
|
self.setter_fn(self.link_name, **{inner_key: value})
|
|
234
228
|
|
|
235
229
|
return set_value
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
iF = TypeVar('iF', bound=InterfaceFactoryTemplate)
|