lsst-pex-config 29.2025.3500__tar.gz → 29.2025.4500__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.
- {lsst_pex_config-29.2025.3500/python/lsst_pex_config.egg-info → lsst_pex_config-29.2025.4500}/PKG-INFO +3 -4
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/pyproject.toml +3 -4
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/config.py +32 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/configChoiceField.py +37 -20
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/configDictField.py +29 -4
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/configField.py +7 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/configurableActions/_configurableAction.py +5 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/configurableActions/_configurableActionStructField.py +14 -1
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/configurableField.py +22 -5
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/dictField.py +21 -2
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/history.py +4 -1
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/listField.py +23 -3
- lsst_pex_config-29.2025.4500/python/lsst/pex/config/version.py +2 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500/python/lsst_pex_config.egg-info}/PKG-INFO +3 -4
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_Config.py +55 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_configurableActions.py +19 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_configurableField.py +15 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_dictField.py +1 -1
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_registry.py +1 -1
- lsst_pex_config-29.2025.3500/python/lsst/pex/config/version.py +0 -2
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/COPYRIGHT +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/LICENSE +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/MANIFEST.in +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/README.rst +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/bsd_license.txt +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/doc/lsst.pex.config/CHANGES.rst +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/doc/lsst.pex.config/design-notes.rst +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/doc/lsst.pex.config/field-types.rst +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/doc/lsst.pex.config/index.rst +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/doc/lsst.pex.config/inspecting-configs.rst +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/doc/lsst.pex.config/overview.rst +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/doc/lsst.pex.config/registry-intro.rst +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/doc/lsst.pex.config/wrapping-cpp-control-objects.rst +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/gpl-v3.0.txt +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/__init__.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/__init__.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/__init__.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/_doNotImportMe.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/callStack.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/choiceField.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/comparison.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/configurableActions/__init__.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/configurableActions/_configurableActionField.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/configurableActions/tests.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/convert.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/py.typed +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/rangeField.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/registry.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/wrap.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst_pex_config.egg-info/SOURCES.txt +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst_pex_config.egg-info/dependency_links.txt +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst_pex_config.egg-info/requires.txt +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst_pex_config.egg-info/top_level.txt +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst_pex_config.egg-info/zip-safe +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/setup.cfg +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/testLib.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test__file__.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_configChoiceField.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_configDictField.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_history.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_listField.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_ticket1911.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_ticket1914.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_ticket1915.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_ticket1929.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_ticket1995.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_ticket2818.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_ticketDM-7337.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_unloaded_yaml.py +0 -0
- {lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_wrap.py +0 -0
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lsst-pex-config
|
|
3
|
-
Version: 29.2025.
|
|
3
|
+
Version: 29.2025.4500
|
|
4
4
|
Summary: A flexible configuration system using Python files.
|
|
5
5
|
Author-email: Rubin Observatory Data Management <dm-admin@lists.lsst.org>
|
|
6
|
-
License: BSD
|
|
6
|
+
License-Expression: BSD-3-Clause OR GPL-3.0-or-later
|
|
7
7
|
Project-URL: Homepage, https://github.com/lsst/pex_config
|
|
8
8
|
Keywords: lsst
|
|
9
9
|
Classifier: Intended Audience :: Science/Research
|
|
10
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
11
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
12
10
|
Classifier: Operating System :: OS Independent
|
|
13
11
|
Classifier: Programming Language :: Python :: 3
|
|
14
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
16
|
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
18
17
|
Requires-Python: >=3.10.0
|
|
19
18
|
Description-Content-Type: text/x-rst
|
|
@@ -6,20 +6,20 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
name = "lsst-pex-config"
|
|
7
7
|
requires-python = ">=3.10.0"
|
|
8
8
|
description = "A flexible configuration system using Python files."
|
|
9
|
-
license =
|
|
9
|
+
license = "BSD-3-Clause OR GPL-3.0-or-later"
|
|
10
|
+
license-files = ["COPYRIGHT", "LICENSE", "gpl-v3.0.txt", "bsd_license.txt"]
|
|
10
11
|
readme = "README.rst"
|
|
11
12
|
authors = [
|
|
12
13
|
{name="Rubin Observatory Data Management", email="dm-admin@lists.lsst.org"},
|
|
13
14
|
]
|
|
14
15
|
classifiers = [
|
|
15
16
|
"Intended Audience :: Science/Research",
|
|
16
|
-
"License :: OSI Approved :: BSD License",
|
|
17
|
-
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
|
|
18
17
|
"Operating System :: OS Independent",
|
|
19
18
|
"Programming Language :: Python :: 3",
|
|
20
19
|
"Programming Language :: Python :: 3.10",
|
|
21
20
|
"Programming Language :: Python :: 3.11",
|
|
22
21
|
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
23
|
"Topic :: Scientific/Engineering :: Astronomy",
|
|
24
24
|
]
|
|
25
25
|
keywords = ["lsst"]
|
|
@@ -43,7 +43,6 @@ where = ["python"]
|
|
|
43
43
|
|
|
44
44
|
[tool.setuptools]
|
|
45
45
|
zip-safe = true
|
|
46
|
-
license-files = ["COPYRIGHT", "LICENSE", "gpl-v3.0.txt", "bsd_license.txt"]
|
|
47
46
|
|
|
48
47
|
[tool.setuptools.package-data]
|
|
49
48
|
"lsst.pex.config" = ["py.typed"]
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/config.py
RENAMED
|
@@ -700,6 +700,14 @@ class Field(Generic[FieldTypeVar]):
|
|
|
700
700
|
"""
|
|
701
701
|
return self.__get__(instance)
|
|
702
702
|
|
|
703
|
+
def _copy_storage(self, old: Config, new: Config) -> Any:
|
|
704
|
+
"""Copy the storage for this field in the given field into an object
|
|
705
|
+
suitable for storage in a new copy of that config.
|
|
706
|
+
|
|
707
|
+
Any frozen storage should be unfrozen.
|
|
708
|
+
"""
|
|
709
|
+
return copy.deepcopy(old._storage[self.name])
|
|
710
|
+
|
|
703
711
|
@overload
|
|
704
712
|
def __get__(
|
|
705
713
|
self, instance: None, owner: Any = None, at: Any = None, label: str = "default"
|
|
@@ -1055,6 +1063,30 @@ class Config(metaclass=ConfigMeta): # type: ignore
|
|
|
1055
1063
|
instance.update(__at=at, **kw)
|
|
1056
1064
|
return instance
|
|
1057
1065
|
|
|
1066
|
+
def copy(self) -> Config:
|
|
1067
|
+
"""Return a deep copy of this config.
|
|
1068
|
+
|
|
1069
|
+
Notes
|
|
1070
|
+
-----
|
|
1071
|
+
The returned config object is not frozen, even if the original was.
|
|
1072
|
+
If a nested config object is copied, it retains the name from its
|
|
1073
|
+
original hierarchy.
|
|
1074
|
+
|
|
1075
|
+
Nested objects are only shared between the new and old configs if they
|
|
1076
|
+
are not possible to modify via the config's interfaces (e.g. entries
|
|
1077
|
+
in the the history list are not copied, but the lists themselves are,
|
|
1078
|
+
so modifications to one copy do not modify the other).
|
|
1079
|
+
"""
|
|
1080
|
+
instance = object.__new__(type(self))
|
|
1081
|
+
instance._frozen = False
|
|
1082
|
+
instance._name = self._name
|
|
1083
|
+
instance._history = {k: list(v) for k, v in self._history.items()}
|
|
1084
|
+
instance._imports = set(self._imports)
|
|
1085
|
+
# Important to set up storage last, since fields sometimes store
|
|
1086
|
+
# proxy objects that reference their parent (especially for history).
|
|
1087
|
+
instance._storage = {k: self._fields[k]._copy_storage(self, instance) for k in self._storage}
|
|
1088
|
+
return instance
|
|
1089
|
+
|
|
1058
1090
|
def __reduce__(self):
|
|
1059
1091
|
"""Reduction for pickling (function with arguments to reproduce).
|
|
1060
1092
|
|
|
@@ -30,7 +30,6 @@ __all__ = ["ConfigChoiceField"]
|
|
|
30
30
|
|
|
31
31
|
import collections.abc
|
|
32
32
|
import copy
|
|
33
|
-
import weakref
|
|
34
33
|
from typing import Any, ForwardRef, overload
|
|
35
34
|
|
|
36
35
|
from .callStack import getCallStack, getStackFrame
|
|
@@ -63,13 +62,19 @@ class SelectionSet(collections.abc.MutableSet):
|
|
|
63
62
|
history.
|
|
64
63
|
"""
|
|
65
64
|
|
|
66
|
-
def __init__(
|
|
65
|
+
def __init__(
|
|
66
|
+
self,
|
|
67
|
+
dict_: ConfigInstanceDict,
|
|
68
|
+
value: Any,
|
|
69
|
+
at=None,
|
|
70
|
+
label: str = "assignment",
|
|
71
|
+
setHistory: bool = True,
|
|
72
|
+
):
|
|
67
73
|
if at is None:
|
|
68
74
|
at = getCallStack()
|
|
69
75
|
self._dict = dict_
|
|
70
76
|
self._field = self._dict._field
|
|
71
|
-
self.
|
|
72
|
-
self.__history = self._config._history.setdefault(self._field.name, [])
|
|
77
|
+
self._history = self._dict._config._history.setdefault(self._field.name, [])
|
|
73
78
|
if value is not None:
|
|
74
79
|
try:
|
|
75
80
|
for v in value:
|
|
@@ -78,20 +83,13 @@ class SelectionSet(collections.abc.MutableSet):
|
|
|
78
83
|
self._dict.__getitem__(v, at=at)
|
|
79
84
|
except TypeError as e:
|
|
80
85
|
msg = f"Value {value} is of incorrect type {_typeStr(value)}. Sequence type expected"
|
|
81
|
-
raise FieldValidationError(self._field, self._config, msg) from e
|
|
86
|
+
raise FieldValidationError(self._field, self._dict._config, msg) from e
|
|
82
87
|
self._set = set(value)
|
|
83
88
|
else:
|
|
84
89
|
self._set = set()
|
|
85
90
|
|
|
86
91
|
if setHistory:
|
|
87
|
-
self.
|
|
88
|
-
|
|
89
|
-
@property
|
|
90
|
-
def _config(self) -> Config:
|
|
91
|
-
# Config Fields should never outlive their config class instance
|
|
92
|
-
# assert that as such here
|
|
93
|
-
assert self._config_() is not None
|
|
94
|
-
return self._config_()
|
|
92
|
+
self._history.append((f"Set selection to {self}", at, label))
|
|
95
93
|
|
|
96
94
|
def add(self, value, at=None):
|
|
97
95
|
"""Add a value to the selected set.
|
|
@@ -104,7 +102,7 @@ class SelectionSet(collections.abc.MutableSet):
|
|
|
104
102
|
optional
|
|
105
103
|
Stack frames for history recording.
|
|
106
104
|
"""
|
|
107
|
-
if self._config._frozen:
|
|
105
|
+
if self._dict._config._frozen:
|
|
108
106
|
raise FieldValidationError(self._field, self._config, "Cannot modify a frozen Config")
|
|
109
107
|
|
|
110
108
|
if at is None:
|
|
@@ -114,7 +112,7 @@ class SelectionSet(collections.abc.MutableSet):
|
|
|
114
112
|
# invoke __getitem__ to make sure it's present
|
|
115
113
|
self._dict.__getitem__(value, at=at)
|
|
116
114
|
|
|
117
|
-
self.
|
|
115
|
+
self._history.append((f"added {value} to selection", at, "selection"))
|
|
118
116
|
self._set.add(value)
|
|
119
117
|
|
|
120
118
|
def discard(self, value, at=None):
|
|
@@ -128,8 +126,8 @@ class SelectionSet(collections.abc.MutableSet):
|
|
|
128
126
|
optional
|
|
129
127
|
Stack frames for history recording.
|
|
130
128
|
"""
|
|
131
|
-
if self._config._frozen:
|
|
132
|
-
raise FieldValidationError(self._field, self._config, "Cannot modify a frozen Config")
|
|
129
|
+
if self._dict._config._frozen:
|
|
130
|
+
raise FieldValidationError(self._field, self._dict._config, "Cannot modify a frozen Config")
|
|
133
131
|
|
|
134
132
|
if value not in self._dict:
|
|
135
133
|
return
|
|
@@ -137,7 +135,7 @@ class SelectionSet(collections.abc.MutableSet):
|
|
|
137
135
|
if at is None:
|
|
138
136
|
at = getCallStack()
|
|
139
137
|
|
|
140
|
-
self.
|
|
138
|
+
self._history.append((f"removed {value} from selection", at, "selection"))
|
|
141
139
|
self._set.discard(value)
|
|
142
140
|
|
|
143
141
|
def __len__(self):
|
|
@@ -177,9 +175,9 @@ class ConfigInstanceDict(collections.abc.Mapping[str, Config]):
|
|
|
177
175
|
(that is, ``typemap[name]``).
|
|
178
176
|
"""
|
|
179
177
|
|
|
180
|
-
def __init__(self, config, field):
|
|
178
|
+
def __init__(self, config: Config, field: ConfigChoiceField):
|
|
181
179
|
collections.abc.Mapping.__init__(self)
|
|
182
|
-
self._dict = {}
|
|
180
|
+
self._dict: dict[str, Config] = {}
|
|
183
181
|
self._selection = None
|
|
184
182
|
self._config = config
|
|
185
183
|
self._field = field
|
|
@@ -187,6 +185,18 @@ class ConfigInstanceDict(collections.abc.Mapping[str, Config]):
|
|
|
187
185
|
self.__doc__ = field.doc
|
|
188
186
|
self._typemap = None
|
|
189
187
|
|
|
188
|
+
def _copy(self, config: Config) -> ConfigInstanceDict:
|
|
189
|
+
result = type(self)(config, self._field)
|
|
190
|
+
result._dict = {k: v.copy() for k, v in self._dict.items()}
|
|
191
|
+
result._history.extend(self._history)
|
|
192
|
+
result._typemap = self._typemap
|
|
193
|
+
if self._selection is not None:
|
|
194
|
+
if self._field.multi:
|
|
195
|
+
result._selection = SelectionSet(result._dict, self._selection._set)
|
|
196
|
+
else:
|
|
197
|
+
result._selection = self._selection
|
|
198
|
+
return result
|
|
199
|
+
|
|
190
200
|
@property
|
|
191
201
|
def types(self):
|
|
192
202
|
return self._typemap if self._typemap is not None else self._field.typemap
|
|
@@ -544,6 +554,13 @@ class ConfigChoiceField(Field[ConfigInstanceDict]):
|
|
|
544
554
|
else:
|
|
545
555
|
instanceDict._setSelection(value, at=at, label=label)
|
|
546
556
|
|
|
557
|
+
def _copy_storage(self, old: Config, new: Config) -> Any:
|
|
558
|
+
instance_dict: ConfigInstanceDict | None = old._storage.get(self.name)
|
|
559
|
+
if instance_dict is not None:
|
|
560
|
+
return instance_dict._copy(new)
|
|
561
|
+
else:
|
|
562
|
+
return None
|
|
563
|
+
|
|
547
564
|
def rename(self, instance):
|
|
548
565
|
instanceDict = self.__get__(instance)
|
|
549
566
|
fullname = _joinNamePath(instance._name, self.name)
|
|
@@ -24,10 +24,13 @@
|
|
|
24
24
|
#
|
|
25
25
|
# You should have received a copy of the GNU General Public License
|
|
26
26
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
27
|
+
from __future__ import annotations
|
|
27
28
|
|
|
28
29
|
__all__ = ["ConfigDictField"]
|
|
29
30
|
|
|
30
|
-
from .
|
|
31
|
+
from collections.abc import Mapping
|
|
32
|
+
|
|
33
|
+
from .callStack import StackFrame, getCallStack, getStackFrame
|
|
31
34
|
from .comparison import compareConfigs, compareScalars, getComparisonName
|
|
32
35
|
from .config import Config, FieldValidationError, _autocast, _joinNamePath, _typeStr
|
|
33
36
|
from .dictField import Dict, DictField
|
|
@@ -51,11 +54,33 @@ class ConfigDict(Dict[str, Config]):
|
|
|
51
54
|
Stack frame for history recording. Will be calculated if `None`.
|
|
52
55
|
label : `str`, optional
|
|
53
56
|
Label to use for history recording.
|
|
57
|
+
setHistory : `bool`, optional
|
|
58
|
+
Whether to append to the history record.
|
|
54
59
|
"""
|
|
55
60
|
|
|
56
|
-
def __init__(
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
def __init__(
|
|
62
|
+
self,
|
|
63
|
+
config: Config,
|
|
64
|
+
field: ConfigDictField,
|
|
65
|
+
value: Mapping[str, Config] | None,
|
|
66
|
+
*,
|
|
67
|
+
at: list[StackFrame] | None,
|
|
68
|
+
label: str,
|
|
69
|
+
setHistory: bool = True,
|
|
70
|
+
):
|
|
71
|
+
Dict.__init__(self, config, field, value, at=at, label=label, setHistory=False)
|
|
72
|
+
if setHistory:
|
|
73
|
+
self.history.append(("Dict initialized", at, label))
|
|
74
|
+
|
|
75
|
+
def _copy(self, config: Config) -> Dict:
|
|
76
|
+
return type(self)(
|
|
77
|
+
config,
|
|
78
|
+
self._field,
|
|
79
|
+
{k: v._copy() for k, v in self._dict.items()},
|
|
80
|
+
at=None,
|
|
81
|
+
label="copy",
|
|
82
|
+
setHistory=False,
|
|
83
|
+
)
|
|
59
84
|
|
|
60
85
|
def __setitem__(self, k, x, at=None, label="setitem", setHistory=True):
|
|
61
86
|
if self._config._frozen:
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/configField.py
RENAMED
|
@@ -243,6 +243,13 @@ class ConfigField(Field[FieldTypeVar]):
|
|
|
243
243
|
value = self.__get__(instance)
|
|
244
244
|
return value.toDict()
|
|
245
245
|
|
|
246
|
+
def _copy_storage(self, old: Config, new: Config) -> Any:
|
|
247
|
+
value: Config | None = old._storage.get(self.name)
|
|
248
|
+
if value is not None:
|
|
249
|
+
return value.copy()
|
|
250
|
+
else:
|
|
251
|
+
return None
|
|
252
|
+
|
|
246
253
|
def validate(self, instance):
|
|
247
254
|
"""Validate the field (for internal use only).
|
|
248
255
|
|
|
@@ -62,3 +62,8 @@ class ConfigurableAction(Config):
|
|
|
62
62
|
|
|
63
63
|
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
|
64
64
|
raise NotImplementedError("This method should be overloaded in subclasses")
|
|
65
|
+
|
|
66
|
+
def copy(self) -> ConfigurableAction:
|
|
67
|
+
result = super().copy()
|
|
68
|
+
result.identity = self.identity
|
|
69
|
+
return result
|
|
@@ -165,12 +165,18 @@ class ConfigurableActionStruct(Generic[ActionTypeVar]):
|
|
|
165
165
|
object.__setattr__(self, "_field", field)
|
|
166
166
|
object.__setattr__(self, "_history", [])
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
if at is not None:
|
|
169
|
+
self.history.append(("Struct initialized", at, label))
|
|
169
170
|
|
|
170
171
|
if value is not None:
|
|
171
172
|
for k, v in value.items():
|
|
172
173
|
setattr(self, k, v)
|
|
173
174
|
|
|
175
|
+
def _copy(self, config: Config) -> ConfigurableActionStruct:
|
|
176
|
+
result = ConfigurableActionStruct(config, self._field, self._attrs, at=None, label="copy")
|
|
177
|
+
result.history.extend(self.history)
|
|
178
|
+
return result
|
|
179
|
+
|
|
174
180
|
@property
|
|
175
181
|
def _config(self) -> Config:
|
|
176
182
|
# Config Fields should never outlive their config class instance
|
|
@@ -391,6 +397,13 @@ class ConfigurableActionStructField(Field[ActionTypeVar]):
|
|
|
391
397
|
|
|
392
398
|
return dict_
|
|
393
399
|
|
|
400
|
+
def _copy_storage(self, old: Config, new: Config) -> ConfigurableActionStruct:
|
|
401
|
+
struct: ConfigurableActionStruct | None = old._storage.get(self.name)
|
|
402
|
+
if struct is not None:
|
|
403
|
+
return struct._copy(new)
|
|
404
|
+
else:
|
|
405
|
+
return None
|
|
406
|
+
|
|
394
407
|
def save(self, outfile, instance):
|
|
395
408
|
actionStruct = self.__get__(instance)
|
|
396
409
|
fullname = _joinNamePath(instance._name, self.name)
|
|
@@ -34,7 +34,7 @@ import weakref
|
|
|
34
34
|
from collections.abc import Mapping
|
|
35
35
|
from typing import Any, Generic, overload
|
|
36
36
|
|
|
37
|
-
from .callStack import getCallStack, getStackFrame
|
|
37
|
+
from .callStack import StackFrame, getCallStack, getStackFrame
|
|
38
38
|
from .comparison import compareConfigs, getComparisonName
|
|
39
39
|
from .config import (
|
|
40
40
|
Config,
|
|
@@ -57,9 +57,9 @@ class ConfigurableInstance(Generic[FieldTypeVar]):
|
|
|
57
57
|
Config to proxy.
|
|
58
58
|
field : `~lsst.pex.config.ConfigurableField`
|
|
59
59
|
Field to use.
|
|
60
|
-
at : `list` of `~lsst.pex.config.callStack.StackFrame` or `None
|
|
60
|
+
at : `list` of `~lsst.pex.config.callStack.StackFrame` or `None`
|
|
61
61
|
Stack frame for history recording. Will be calculated if `None`.
|
|
62
|
-
label : `str
|
|
62
|
+
label : `str`
|
|
63
63
|
Label to use for history recording.
|
|
64
64
|
|
|
65
65
|
Notes
|
|
@@ -74,7 +74,7 @@ class ConfigurableInstance(Generic[FieldTypeVar]):
|
|
|
74
74
|
using the ``target`` property.
|
|
75
75
|
"""
|
|
76
76
|
|
|
77
|
-
def __initValue(self, at, label):
|
|
77
|
+
def __initValue(self, at: list[StackFrame] | None, label: str, setHistory: bool = True):
|
|
78
78
|
"""Construct value of field.
|
|
79
79
|
|
|
80
80
|
Notes
|
|
@@ -94,7 +94,7 @@ class ConfigurableInstance(Generic[FieldTypeVar]):
|
|
|
94
94
|
def __init__(self, config, field, at=None, label="default"):
|
|
95
95
|
object.__setattr__(self, "_config_", weakref.ref(config))
|
|
96
96
|
object.__setattr__(self, "_field", field)
|
|
97
|
-
object.__setattr__(self, "__doc__",
|
|
97
|
+
object.__setattr__(self, "__doc__", field.doc)
|
|
98
98
|
object.__setattr__(self, "_target", field.target)
|
|
99
99
|
object.__setattr__(self, "_ConfigClass", field.ConfigClass)
|
|
100
100
|
object.__setattr__(self, "_value", None)
|
|
@@ -107,6 +107,16 @@ class ConfigurableInstance(Generic[FieldTypeVar]):
|
|
|
107
107
|
history = config._history.setdefault(field.name, [])
|
|
108
108
|
history.append(("Targeted and initialized from defaults", at, label))
|
|
109
109
|
|
|
110
|
+
def _copy(self, parent: Config) -> ConfigurableInstance:
|
|
111
|
+
result = object.__new__(ConfigurableInstance)
|
|
112
|
+
object.__setattr__(result, "_config_", weakref.ref(parent))
|
|
113
|
+
object.__setattr__(result, "_field", self._field)
|
|
114
|
+
object.__setattr__(result, "__doc__", self.__doc__)
|
|
115
|
+
object.__setattr__(result, "_target", self._target)
|
|
116
|
+
object.__setattr__(result, "_ConfigClass", self._ConfigClass)
|
|
117
|
+
object.__setattr__(result, "_value", self._value.copy())
|
|
118
|
+
return result
|
|
119
|
+
|
|
110
120
|
@property
|
|
111
121
|
def _config(self) -> Config:
|
|
112
122
|
# Config Fields should never outlive their config class instance
|
|
@@ -430,6 +440,13 @@ class ConfigurableField(Field[ConfigurableInstance[FieldTypeVar]]):
|
|
|
430
440
|
value = self.__get__(instance)
|
|
431
441
|
return value.toDict()
|
|
432
442
|
|
|
443
|
+
def _copy_storage(self, old: Config, new: Config) -> ConfigurableInstance | None:
|
|
444
|
+
instance: ConfigurableInstance | None = old._storage.get(self.name)
|
|
445
|
+
if instance is not None:
|
|
446
|
+
return instance._copy(new)
|
|
447
|
+
else:
|
|
448
|
+
return None
|
|
449
|
+
|
|
433
450
|
def validate(self, instance):
|
|
434
451
|
value = self.__get__(instance)
|
|
435
452
|
value.validate()
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/dictField.py
RENAMED
|
@@ -34,7 +34,7 @@ import weakref
|
|
|
34
34
|
from collections.abc import Iterator, Mapping
|
|
35
35
|
from typing import Any, ForwardRef, Generic, TypeVar, cast
|
|
36
36
|
|
|
37
|
-
from .callStack import getCallStack, getStackFrame
|
|
37
|
+
from .callStack import StackFrame, getCallStack, getStackFrame
|
|
38
38
|
from .comparison import compareScalars, getComparisonName
|
|
39
39
|
from .config import (
|
|
40
40
|
Config,
|
|
@@ -71,7 +71,16 @@ class Dict(collections.abc.MutableMapping[KeyTypeVar, ItemTypeVar]):
|
|
|
71
71
|
Whether to append to the history record.
|
|
72
72
|
"""
|
|
73
73
|
|
|
74
|
-
def __init__(
|
|
74
|
+
def __init__(
|
|
75
|
+
self,
|
|
76
|
+
config: Config,
|
|
77
|
+
field: DictField,
|
|
78
|
+
value: Mapping[KeyTypeVar, ItemTypeVar],
|
|
79
|
+
*,
|
|
80
|
+
at: list[StackFrame] | None,
|
|
81
|
+
label: str,
|
|
82
|
+
setHistory: bool = True,
|
|
83
|
+
):
|
|
75
84
|
self._field = field
|
|
76
85
|
self._config_ = weakref.ref(config)
|
|
77
86
|
self._dict = {}
|
|
@@ -100,6 +109,9 @@ class Dict(collections.abc.MutableMapping[KeyTypeVar, ItemTypeVar]):
|
|
|
100
109
|
"""History (read-only).
|
|
101
110
|
"""
|
|
102
111
|
|
|
112
|
+
def _copy(self, config: Config) -> Dict:
|
|
113
|
+
return type(self)(config, self._field, self._dict.copy(), at=None, label="copy", setHistory=False)
|
|
114
|
+
|
|
103
115
|
def __getitem__(self, k: KeyTypeVar) -> ItemTypeVar:
|
|
104
116
|
return self._dict[k]
|
|
105
117
|
|
|
@@ -403,6 +415,13 @@ class DictField(Field[Dict[KeyTypeVar, ItemTypeVar]], Generic[KeyTypeVar, ItemTy
|
|
|
403
415
|
value = self.__get__(instance)
|
|
404
416
|
return dict(value) if value is not None else None
|
|
405
417
|
|
|
418
|
+
def _copy_storage(self, old: Config, new: Config) -> Dict[KeyTypeVar, ItemTypeVar] | None:
|
|
419
|
+
value: Dict[KeyTypeVar, ItemTypeVar] | None = old._storage[self.name]
|
|
420
|
+
if value is not None:
|
|
421
|
+
return value._copy(new)
|
|
422
|
+
else:
|
|
423
|
+
return None
|
|
424
|
+
|
|
406
425
|
def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
|
|
407
426
|
"""Compare two fields for equality.
|
|
408
427
|
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/history.py
RENAMED
|
@@ -252,7 +252,10 @@ def format(config, name=None, writeSourceLine=True, prefix="", verbose=False):
|
|
|
252
252
|
fullname = f"{config._name}.{name}" if config._name is not None else name
|
|
253
253
|
msg.append(_colorize(re.sub(r"^root\.", "", fullname), "NAME"))
|
|
254
254
|
for value, output in outputs:
|
|
255
|
-
|
|
255
|
+
if value is not None:
|
|
256
|
+
line = prefix + _colorize(f"{value:<{valueLength}}", "VALUE") + " "
|
|
257
|
+
else:
|
|
258
|
+
line = prefix + _colorize("None", "VALUE") + " "
|
|
256
259
|
for i, vt in enumerate(output):
|
|
257
260
|
if writeSourceLine:
|
|
258
261
|
vt[0][0] = f"{vt[0][0]:<{sourceLength}}"
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/listField.py
RENAMED
|
@@ -25,14 +25,16 @@
|
|
|
25
25
|
# You should have received a copy of the GNU General Public License
|
|
26
26
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
27
27
|
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
28
30
|
__all__ = ["ListField"]
|
|
29
31
|
|
|
30
32
|
import collections.abc
|
|
31
33
|
import weakref
|
|
32
|
-
from collections.abc import Iterable, MutableSequence
|
|
34
|
+
from collections.abc import Iterable, MutableSequence, Sequence
|
|
33
35
|
from typing import Any, Generic, overload
|
|
34
36
|
|
|
35
|
-
from .callStack import getCallStack, getStackFrame
|
|
37
|
+
from .callStack import StackFrame, getCallStack, getStackFrame
|
|
36
38
|
from .comparison import compareScalars, getComparisonName
|
|
37
39
|
from .config import (
|
|
38
40
|
Config,
|
|
@@ -73,7 +75,15 @@ class List(collections.abc.MutableSequence[FieldTypeVar]):
|
|
|
73
75
|
`ListField.itemCheck` method of the ``field`` parameter.
|
|
74
76
|
"""
|
|
75
77
|
|
|
76
|
-
def __init__(
|
|
78
|
+
def __init__(
|
|
79
|
+
self,
|
|
80
|
+
config: Config,
|
|
81
|
+
field: ListField,
|
|
82
|
+
value: Sequence[FieldTypeVar],
|
|
83
|
+
at: list[StackFrame] | None,
|
|
84
|
+
label: str,
|
|
85
|
+
setHistory: bool = True,
|
|
86
|
+
):
|
|
77
87
|
self._field = field
|
|
78
88
|
self._config_ = weakref.ref(config)
|
|
79
89
|
self._history = self._config._history.setdefault(self._field.name, [])
|
|
@@ -133,6 +143,9 @@ class List(collections.abc.MutableSequence[FieldTypeVar]):
|
|
|
133
143
|
"""Read-only history.
|
|
134
144
|
"""
|
|
135
145
|
|
|
146
|
+
def _copy(self, config: Config) -> List:
|
|
147
|
+
return type(self)(config, self._field, self._list.copy(), at=None, label="copy", setHistory=False)
|
|
148
|
+
|
|
136
149
|
def __contains__(self, x):
|
|
137
150
|
return x in self._list
|
|
138
151
|
|
|
@@ -463,6 +476,13 @@ class ListField(Field[List[FieldTypeVar]], Generic[FieldTypeVar]):
|
|
|
463
476
|
value = self.__get__(instance)
|
|
464
477
|
return list(value) if value is not None else None
|
|
465
478
|
|
|
479
|
+
def _copy_storage(self, old: Config, new: Config) -> List[FieldTypeVar] | None:
|
|
480
|
+
value: List[FieldTypeVar] | None = old._storage[self.name]
|
|
481
|
+
if value is not None:
|
|
482
|
+
return value._copy(new)
|
|
483
|
+
else:
|
|
484
|
+
return None
|
|
485
|
+
|
|
466
486
|
def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
|
|
467
487
|
"""Compare two config instances for equality with respect to this
|
|
468
488
|
field.
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lsst-pex-config
|
|
3
|
-
Version: 29.2025.
|
|
3
|
+
Version: 29.2025.4500
|
|
4
4
|
Summary: A flexible configuration system using Python files.
|
|
5
5
|
Author-email: Rubin Observatory Data Management <dm-admin@lists.lsst.org>
|
|
6
|
-
License: BSD
|
|
6
|
+
License-Expression: BSD-3-Clause OR GPL-3.0-or-later
|
|
7
7
|
Project-URL: Homepage, https://github.com/lsst/pex_config
|
|
8
8
|
Keywords: lsst
|
|
9
9
|
Classifier: Intended Audience :: Science/Research
|
|
10
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
11
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
12
10
|
Classifier: Operating System :: OS Independent
|
|
13
11
|
Classifier: Programming Language :: Python :: 3
|
|
14
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
16
|
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
18
17
|
Requires-Python: >=3.10.0
|
|
19
18
|
Description-Content-Type: text/x-rst
|
|
@@ -639,6 +639,61 @@ except ImportError:
|
|
|
639
639
|
else:
|
|
640
640
|
self.assertEqual(v, v1)
|
|
641
641
|
|
|
642
|
+
def test_copy(self):
|
|
643
|
+
"""Test the copy method."""
|
|
644
|
+
self.comp.freeze()
|
|
645
|
+
copy1 = self.comp.copy()
|
|
646
|
+
copy1.c.f = 6.0
|
|
647
|
+
self.assertEqual(copy1.c.f, 6.0)
|
|
648
|
+
self.assertEqual(self.comp.c.f, 0.0)
|
|
649
|
+
copy1.r["AAA"].i = 1
|
|
650
|
+
self.assertEqual(copy1.r["AAA"].i, 1)
|
|
651
|
+
self.assertIsNone(self.comp.r["AAA"].i)
|
|
652
|
+
copy1.r["AAA"].f = 2.0
|
|
653
|
+
self.assertEqual(copy1.r["AAA"].f, 2.0)
|
|
654
|
+
self.assertEqual(self.comp.r["AAA"].f, 3.0)
|
|
655
|
+
copy1.r["AAA"].c = "World"
|
|
656
|
+
self.assertEqual(copy1.r["AAA"].c, "World")
|
|
657
|
+
self.assertEqual(self.comp.r["AAA"].c, "Hello")
|
|
658
|
+
copy1.r["AAA"].r = 4.0
|
|
659
|
+
self.assertEqual(copy1.r["AAA"].r, 4.0)
|
|
660
|
+
self.assertEqual(self.comp.r["AAA"].r, 3.0)
|
|
661
|
+
copy1.r["AAA"].ll.append(4)
|
|
662
|
+
self.assertEqual(copy1.r["AAA"].ll, [1, 2, 3, 4])
|
|
663
|
+
self.assertEqual(self.comp.r["AAA"].ll, [1, 2, 3])
|
|
664
|
+
copy1.r["AAA"].d["key2"] = "value2"
|
|
665
|
+
self.assertEqual(copy1.r["AAA"].d, {"key": "value", "key2": "value2"})
|
|
666
|
+
self.assertEqual(self.comp.r["AAA"].d, {"key": "value"})
|
|
667
|
+
copy1.r.name = "BBB"
|
|
668
|
+
self.assertEqual(copy1.r.name, "BBB")
|
|
669
|
+
self.assertEqual(self.comp.r.name, "AAA")
|
|
670
|
+
copy1.p.name = None
|
|
671
|
+
self.assertIsNone(copy1.p.name)
|
|
672
|
+
self.assertEqual(self.comp.p.name, "BBB")
|
|
673
|
+
# Copy again to avoid shortcuts for default nested objects.
|
|
674
|
+
copy1.freeze()
|
|
675
|
+
copy2 = copy1.copy()
|
|
676
|
+
copy2.c.f = 7.0
|
|
677
|
+
self.assertEqual(copy2.c.f, 7.0)
|
|
678
|
+
self.assertEqual(copy1.c.f, 6.0)
|
|
679
|
+
self.assertEqual(self.comp.c.f, 0.0)
|
|
680
|
+
copy2.r["AAA"].ll.append(5)
|
|
681
|
+
self.assertEqual(copy2.r["AAA"].ll, [1, 2, 3, 4, 5])
|
|
682
|
+
self.assertEqual(copy1.r["AAA"].ll, [1, 2, 3, 4])
|
|
683
|
+
self.assertEqual(self.comp.r["AAA"].ll, [1, 2, 3])
|
|
684
|
+
del copy2.r["AAA"].d["key"]
|
|
685
|
+
self.assertEqual(copy2.r["AAA"].d, {"key2": "value2"})
|
|
686
|
+
self.assertEqual(copy1.r["AAA"].d, {"key": "value", "key2": "value2"})
|
|
687
|
+
self.assertEqual(self.comp.r["AAA"].d, {"key": "value"})
|
|
688
|
+
copy2.r.name = "AAA"
|
|
689
|
+
self.assertEqual(copy2.r.name, "AAA")
|
|
690
|
+
self.assertEqual(copy1.r.name, "BBB")
|
|
691
|
+
self.assertEqual(self.comp.r.name, "AAA")
|
|
692
|
+
copy2.p.name = "AAA"
|
|
693
|
+
self.assertEqual(copy2.p.name, "AAA")
|
|
694
|
+
self.assertIsNone(copy1.p.name)
|
|
695
|
+
self.assertEqual(self.comp.p.name, "BBB")
|
|
696
|
+
|
|
642
697
|
|
|
643
698
|
if __name__ == "__main__":
|
|
644
699
|
unittest.main()
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_configurableActions.py
RENAMED
|
@@ -251,6 +251,25 @@ class ConfigurableActionsTestCase(unittest.TestCase):
|
|
|
251
251
|
config = configClass()
|
|
252
252
|
self.assertEqual(config.toDict(), {"actions": {"test1": {"var": 0}}, "singleAction": {"var": 0}})
|
|
253
253
|
|
|
254
|
+
def test_copy(self):
|
|
255
|
+
"""Test copying a Config with ConfigurableActions."""
|
|
256
|
+
config_cls = self._createConfig(default={"test1": ActionTest1}, singleDefault=ActionTest1)
|
|
257
|
+
config = config_cls()
|
|
258
|
+
copy1 = config.copy()
|
|
259
|
+
copy1.actions.test2 = ActionTest2
|
|
260
|
+
self.assertEqual(tuple(copy1.actions.fieldNames), ("test1", "test2"))
|
|
261
|
+
self.assertEqual(copy1.actions.test2.var, 1)
|
|
262
|
+
self.assertEqual(tuple(config.actions.fieldNames), ("test1",))
|
|
263
|
+
copy1.actions.test1.var = 50
|
|
264
|
+
self.assertEqual(copy1.actions.test1.var, 50)
|
|
265
|
+
self.assertEqual(config.actions.test1.var, 0)
|
|
266
|
+
copy2 = copy1.copy()
|
|
267
|
+
copy2.actions.test3 = ActionTest3()
|
|
268
|
+
self.assertEqual(tuple(copy2.actions.fieldNames), ("test1", "test2", "test3"))
|
|
269
|
+
self.assertEqual(tuple(copy1.actions.fieldNames), ("test1", "test2"))
|
|
270
|
+
self.assertEqual(tuple(config.actions.fieldNames), ("test1",))
|
|
271
|
+
self.assertEqual(copy2.actions.test3.var, 3)
|
|
272
|
+
|
|
254
273
|
|
|
255
274
|
if __name__ == "__main__":
|
|
256
275
|
unittest.main()
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_configurableField.py
RENAMED
|
@@ -157,6 +157,21 @@ class ConfigurableFieldTest(unittest.TestCase):
|
|
|
157
157
|
with self.assertRaises(pexConf.UnexpectedProxyUsageError):
|
|
158
158
|
pickle.dumps(c.c2)
|
|
159
159
|
|
|
160
|
+
def test_copy(self):
|
|
161
|
+
"""Test copying a frozen ConfigurableField."""
|
|
162
|
+
c1 = Config2()
|
|
163
|
+
c1.freeze()
|
|
164
|
+
c2 = c1.copy()
|
|
165
|
+
c2.c1.f = 6.0
|
|
166
|
+
self.assertEqual(c2.c1.f, 6.0)
|
|
167
|
+
self.assertEqual(c1.c1.f, 5.0)
|
|
168
|
+
c2.freeze()
|
|
169
|
+
c3 = c2.copy()
|
|
170
|
+
c3.c1.f = 7.0
|
|
171
|
+
self.assertEqual(c3.c1.f, 7.0)
|
|
172
|
+
self.assertEqual(c2.c1.f, 6.0)
|
|
173
|
+
self.assertEqual(c1.c1.f, 5.0)
|
|
174
|
+
|
|
160
175
|
|
|
161
176
|
if __name__ == "__main__":
|
|
162
177
|
unittest.main()
|
|
@@ -111,7 +111,7 @@ class ConfigTest(unittest.TestCase):
|
|
|
111
111
|
|
|
112
112
|
def testReplace(self):
|
|
113
113
|
"""Test replacement in registry (should always fail)."""
|
|
114
|
-
self.assertRaises(
|
|
114
|
+
self.assertRaises(RuntimeError, self.registry.register, "foo1", self.fooAlg2Class)
|
|
115
115
|
self.assertEqual(self.registry["foo1"], self.fooAlg1Class)
|
|
116
116
|
|
|
117
117
|
def testNesting(self):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/doc/lsst.pex.config/CHANGES.rst
RENAMED
|
File without changes
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/doc/lsst.pex.config/design-notes.rst
RENAMED
|
File without changes
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/doc/lsst.pex.config/field-types.rst
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/doc/lsst.pex.config/overview.rst
RENAMED
|
File without changes
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/doc/lsst.pex.config/registry-intro.rst
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/callStack.py
RENAMED
|
File without changes
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/choiceField.py
RENAMED
|
File without changes
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/comparison.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/convert.py
RENAMED
|
File without changes
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/py.typed
RENAMED
|
File without changes
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/rangeField.py
RENAMED
|
File without changes
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/registry.py
RENAMED
|
File without changes
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/python/lsst/pex/config/wrap.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lsst_pex_config-29.2025.3500 → lsst_pex_config-29.2025.4500}/tests/test_configChoiceField.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|