21from __future__
import annotations
23__all__ = (
"ConfigurableActionStruct",
"ConfigurableActionStructField")
27from types
import GenericAlias, SimpleNamespace
28from typing
import Any, Generic, TypeVar, overload
34from .
import ActionTypeVar, ConfigurableAction
38 """Abstract the logic of using a dictionary to update a
39 `ConfigurableActionStruct` through attribute assignment.
41 This is useful in the context of setting configuration through pipelines
42 or on the command line.
47 instance: ConfigurableActionStruct,
48 value: Mapping[str, ConfigurableAction] | ConfigurableActionStruct,
50 if isinstance(value, Mapping):
52 elif isinstance(value, ConfigurableActionStruct):
58 "Can only update a ConfigurableActionStruct with an instance of such, or a mapping"
60 for name, action
in value.items():
61 setattr(instance, name, action)
63 def __get__(self, instance, objtype=None) -> None:
69 """Abstract the logic of removing an iterable of action names from a
70 `ConfigurableActionStruct` at one time using attribute assignment.
72 This is useful in the context of setting configuration through pipelines
73 or on the command line.
78 Raised if an attribute specified for removal does not exist in the
79 ConfigurableActionStruct
82 def __set__(self, instance: ConfigurableActionStruct, value: str | Iterable[str]) ->
None:
86 if isinstance(value, str):
89 delattr(instance, name)
91 def __get__(self, instance, objtype=None) -> None:
97 """A ConfigurableActionStruct is the storage backend class that supports
98 the ConfigurableActionStructField. This class should not be created
101 This class allows managing a collection of `ConfigurableAction` with a
102 struct like interface, that is to say in an attribute like notation.
106 config : `~lsst.pex.config.Config`
108 field : `ConfigurableActionStructField`
110 value : `~collections.abc.Mapping` [`str`, `ConfigurableAction`]
112 at : `list` of `~lsst.pex.config.callStack.StackFrame` or `None`, optional
113 Stack frames to use for history recording.
114 label : `str`, optional
115 Label to use for history recording.
119 Attributes can be dynamically added or removed as such:
121 .. code-block:: python
123 ConfigurableActionStructInstance.variable1 = a_configurable_action
124 del ConfigurableActionStructInstance.variable1
126 Each action is then available to be individually configured as a normal
127 `lsst.pex.config.Config` object.
129 `ConfigurableActionStruct` supports two special convenience attributes.
131 The first is ``update``. You may assign a dict of `ConfigurableAction` or a
132 `ConfigurableActionStruct` to this attribute which will update the
133 `ConfigurableActionStruct` on which the attribute is invoked such that it
134 will be updated to contain the entries specified by the structure on the
135 right hand side of the equals sign.
137 The second convenience attribute is named ``remove``. You may assign an
138 iterable of strings which correspond to attribute names on the
139 `ConfigurableActionStruct`. All of the corresponding attributes will then
140 be removed. If any attribute does not exist, an `AttributeError` will be
141 raised. Any attributes in the Iterable prior to the name which raises will
142 have been removed from the `ConfigurableActionStruct`
146 _config_: weakref.ref
147 _attrs: dict[str, ActionTypeVar]
148 _field: ConfigurableActionStructField
149 _history: list[tuple]
158 field: ConfigurableActionStructField,
159 value: Mapping[str, ConfigurableAction],
163 object.__setattr__(self,
"_config_", weakref.ref(config))
164 object.__setattr__(self,
"_attrs", {})
165 object.__setattr__(self,
"_field", field)
166 object.__setattr__(self,
"_history", [])
169 self.
history.append((
"Struct initialized", at, label))
171 if value
is not None:
172 for k, v
in value.items():
175 def _copy(self, config: Config) -> ConfigurableActionStruct:
185 assert value
is not None
199 value: ActionTypeVar | type[ActionTypeVar],
205 msg = f
"Cannot modify a frozen Config. Attempting to set item {attr} to value {value}"
210 if not attr.isidentifier():
211 raise ValueError(
"Names used in ConfigurableStructs must be valid as python variable names")
213 if attr
not in (self.__dict__.keys() | type(self).__dict__.keys()):
218 if isinstance(value, ConfigurableAction):
219 valueInst = type(value)(__name=name, __at=at, __label=label, **value._storage)
221 valueInst = value(__name=name, __at=at, __label=label)
222 self.
_attrs[attr] = valueInst
227 if attr
in object.__getattribute__(self,
"_attrs"):
228 result = self.
_attrs[attr]
229 result.identity = attr
232 super().__getattribute__(attr)
242 yield getattr(self, name)
244 def items(self) -> Iterable[tuple[str, ActionTypeVar]]:
246 yield name, getattr(self, name)
252T = TypeVar(
"T", bound=
"ConfigurableActionStructField")
256 """`ConfigurableActionStructField` is a `~lsst.pex.config.Field` subclass
257 that allows a `ConfigurableAction` to be organized in a
258 `~lsst.pex.config.Config` class in a manner similar to how a
259 `~lsst.pipe.base.Struct` works.
261 This class uses a `ConfigurableActionStruct` as an intermediary object to
262 organize the `ConfigurableAction`. See its documentation for further
268 Documentation string.
269 default : `~collections.abc.Mapping` [ `str`, `ConfigurableAction` ] \
272 optional : `bool`, optional
273 If `True`, the field doesn't need to have a set value.
274 deprecated : `bool` or `None`, optional
275 A description of why this Field is deprecated, including removal date.
276 If not `None`, the string is appended to the docstring for this Field.
281 StructClass = ConfigurableActionStruct
286 default: Mapping[str, ConfigurableAction] |
None
291 default: Mapping[str, ConfigurableAction] |
None =
None,
292 optional: bool =
False,
295 source = getStackFrame()
298 dtype=self.__class__,
303 deprecated=deprecated,
307 return GenericAlias(cls, params)
314 | Mapping[str, ConfigurableAction]
316 | ConfigurableActionStruct
317 | ConfigurableActionStructField
318 | type[ConfigurableActionStructField]
320 at: Iterable[StackFrame] =
None,
321 label: str =
"assigment",
324 msg = f
"Cannot modify a frozen Config. Attempting to set field to value {value}"
330 if value
is None or (self.
default is not None and self.
default == value):
331 value = self.
StructClass(instance, self, value, at=at, label=label)
337 value = self.
StructClass(instance, self, value._attrs, at=at, label=label)
338 elif isinstance(value, SimpleNamespace):
341 value = self.
StructClass(instance, self, vars(value), at=at, label=label)
343 elif type(value)
is ConfigurableActionStructField:
345 "ConfigurableActionStructFields can only be used in a class body declaration"
346 f
"Use a {self.StructClass}, SimpleNamespace or Struct"
349 raise ValueError(f
"Unrecognized value {value}, cannot be assigned to this field")
351 history = instance._history.setdefault(self.name, [])
352 history.append((value, at, label))
354 if not isinstance(value, ConfigurableActionStruct):
356 self, instance,
"Can only assign things that are subclasses of Configurable Action"
358 instance._storage[self.name] = value
362 self, instance: None, owner: Any =
None, at: Any =
None, label: str =
"default"
363 ) -> ConfigurableActionStruct[ActionTypeVar]: ...
367 self, instance: Config, owner: Any =
None, at: Any =
None, label: str =
"default"
368 ) -> ConfigurableActionStruct[ActionTypeVar]: ...
370 def __get__(self, instance, owner=None, at=None, label="default"):
371 if instance
is None or not isinstance(instance, Config):
374 field: ConfigurableActionStruct |
None = instance._storage[self.name]
378 actionStruct: ConfigurableActionStruct = self.
__get__(instance)
379 if actionStruct
is not None:
380 for k, v
in actionStruct.items():
387 if value
is not None:
392 actionStruct = self.
__get__(instance)
393 if actionStruct
is None:
396 dict_ = {k: v.toDict()
for k, v
in actionStruct.items()}
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)
407 def save(self, outfile, instance):
408 actionStruct = self.
__get__(instance)
412 outfile.write(f
"{fullname}=None\n")
414 if actionStruct
is None:
417 for _, v
in sorted(actionStruct.items()):
418 outfile.write(f
"{v._name}={_typeStr(v)}()\n")
422 actionStruct = self.
__get__(instance)
423 if actionStruct
is not None:
424 for v
in actionStruct:
429 actionStruct = self.
__get__(instance)
430 for v
in actionStruct:
432 imports |= v._imports
434 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
435 """Compare two fields for equality.
439 instance1 : `lsst.pex.config.Config`
440 Left-hand side config instance to compare.
441 instance2 : `lsst.pex.config.Config`
442 Right-hand side config instance to compare.
444 If `True`, this function returns as soon as an inequality if found.
446 Relative tolerance for floating point comparisons.
448 Absolute tolerance for floating point comparisons.
450 A callable that takes a string, used (possibly repeatedly) to
456 `True` if the fields are equal, `False` otherwise.
460 Floating point comparisons are performed by `numpy.allclose`.
462 d1: ConfigurableActionStruct = getattr(instance1, self.name)
463 d2: ConfigurableActionStruct = getattr(instance2, self.name)
467 if not compareScalars(f
"{name} (fields)", set(d1.fieldNames), set(d2.fieldNames), output=output):
470 for k, v1
in d1.items():
473 f
"{name}.{k}", v1, v2, shortcut=shortcut, rtol=rtol, atol=atol, output=output
475 if not result
and shortcut:
477 equal = equal
and result
Field[FieldTypeVar] __get__(self, None instance, Any owner=None, Any at=None, str label="default")
_setup(self, doc, dtype, default, check, optional, source, deprecated)
rename(self, Config instance)
save(self, outfile, instance)
__class_getitem__(cls, params)
ConfigurableActionStruct[ActionTypeVar] __get__(self, None instance, Any owner=None, Any at=None, str label="default")
__set__(self, Config instance,(None|Mapping[str, ConfigurableAction]|SimpleNamespace|ConfigurableActionStruct|ConfigurableActionStructField|type[ConfigurableActionStructField]) value, Iterable[StackFrame] at=None, str label="assigment")
_collectImports(self, instance, imports)
ConfigurableActionStruct _copy_storage(self, Config old, Config new)
_compare(self, instance1, instance2, shortcut, rtol, atol, output)
__init__(self, str doc, Mapping[str, ConfigurableAction]|None default=None, bool optional=False, deprecated=None)
Iterable[tuple[str, ActionTypeVar]] items(self)
Any __getattr__(self, attr)
None __setattr__(self, str attr, ActionTypeVar|type[ActionTypeVar] value, at=None, label="setattr", setHistory=False)
__init__(self, Config config, ConfigurableActionStructField field, Mapping[str, ConfigurableAction] value, Any at, str label)
Iterator[ActionTypeVar] __iter__(self)
ConfigurableActionStruct _copy(self, Config config)
None __set__(self, ConfigurableActionStruct instance, str|Iterable[str] value)
None __get__(self, instance, objtype=None)
None __get__(self, instance, objtype=None)
None __set__(self, ConfigurableActionStruct instance, Mapping[str, ConfigurableAction]|ConfigurableActionStruct value)
compareConfigs(name, c1, c2, shortcut=True, rtol=1e-8, atol=1e-8, output=None)
getComparisonName(name1, name2)
compareScalars(name, v1, v2, output, rtol=1e-8, atol=1e-8, dtype=None)
_joinNamePath(prefix=None, name=None, index=None)