27from __future__
import annotations
29__all__ = [
"ConfigChoiceField"]
34from typing
import Any, ForwardRef, overload
36from .callStack
import getCallStack, getStackFrame
37from .comparison
import compareConfigs, compareScalars, getComparisonName
38from .config
import Config, Field, FieldValidationError, UnexpectedProxyUsageError, _joinNamePath, _typeStr
42 """A mutable set class that tracks the selection of multi-select
43 `~lsst.pex.config.ConfigChoiceField` objects.
47 dict_ : `ConfigInstanceDict`
48 The dictionary of instantiated configs.
51 at : `list` of `~lsst.pex.config.callStack.StackFrame` or `None`, optional
52 The call stack when the selection was made.
53 label : `str`, optional
54 Label for history tracking.
55 setHistory : `bool`, optional
56 Add this even to the history, if `True`.
60 This class allows a user of a multi-select
61 `~lsst.pex.config.ConfigChoiceField` to add or discard items from the set
62 of active configs. Each change to the selection is tracked in the field's
66 def __init__(self, dict_, value, at=None, label="assignment", setHistory=True):
76 if v
not in self.
_dict:
78 self.
_dict.__getitem__(v, at=at)
80 msg = f
"Value {value} is of incorrect type {_typeStr(value)}. Sequence type expected"
87 self.
__history.append((
"Set selection to %s" % self, at, label))
96 def add(self, value, at=None):
97 """Add a value to the selected set.
101 value : `~typing.Any`
103 at : `list` of `~lsst.pex.config.callStack.StackFrame` or `None`,\
105 Stack frames for history recording.
113 if value
not in self.
_dict:
115 self.
_dict.__getitem__(value, at=at)
117 self.
__history.append((
"added %s to selection" % value, at,
"selection"))
121 """Discard a value from the selected set.
125 value : `~typing.Any`
127 at : `list` of `~lsst.pex.config.callStack.StackFrame` or `None`,\
129 Stack frames for history recording.
134 if value
not in self.
_dict:
140 self.
__history.append((
"removed %s from selection" % value, at,
"selection"))
144 return len(self.
_set)
147 return iter(self.
_set)
150 return value
in self.
_set
153 return repr(list(self.
_set))
156 return str(list(self.
_set))
160 f
"Proxy container for config field {self._field.name} cannot "
161 "be pickled; it should be converted to a built-in container before "
162 "being assigned to other objects or variables."
167 """Dictionary of instantiated configs, used to populate a
168 `~lsst.pex.config.ConfigChoiceField`.
172 config : `lsst.pex.config.Config`
173 A configuration instance.
174 field : `lsst.pex.config.Field`-type
175 A configuration field. Note that the `lsst.pex.config.Field.fieldmap`
176 attribute must provide key-based access to configuration classes,
177 (that is, ``typemap[name]``).
181 collections.abc.Mapping.__init__(self)
215 if value
not in self.
_dict:
223 self.
_field, self.
_config,
"Single-selection field has no attribute 'names'"
230 self.
_field, self.
_config,
"Single-selection field has no attribute 'names'"
237 self.
_field, self.
_config,
"Single-selection field has no attribute 'names'"
244 self.
_field, self.
_config,
"Multi-selection field has no attribute 'name'"
251 self.
_field, self.
_config,
"Multi-selection field has no attribute 'name'"
258 self.
_field, self.
_config,
"Multi-selection field has no attribute 'name'"
262 names = property(_getNames, _setNames, _delNames)
263 """List of names of active items in a multi-selection
264 ``ConfigInstanceDict``. Disabled in a single-selection ``_Registry``; use
265 the `name` attribute instead.
268 name = property(_getName, _setName, _delName)
269 """Name of the active item in a single-selection ``ConfigInstanceDict``.
270 Disabled in a multi-selection ``_Registry``; use the ``names`` attribute
283 active = property(_getActive)
284 """The selected items.
286 For multi-selection, this is equivalent to: ``[self[name] for name in
287 self.names]``. For single-selection, this is equivalent to: ``self[name]``.
292 value = self.
_dict[k]
298 self.
_field, self.
_config,
"Unknown key %r in Registry/ConfigChoiceField" % k
300 name = _joinNamePath(self.
_config._name, self.
_field.name, k)
303 at.insert(0, dtype._source)
304 value = self.
_dict.setdefault(k, dtype(__name=name, __at=at, __label=label))
316 if value != dtype
and type(value)
is not dtype:
317 msg =
"Value {} at key {} is of incorrect type {}. Expected type {}".format(
327 name = _joinNamePath(self.
_config._name, self.
_field.name, k)
328 oldValue = self.
_dict.get(k,
None)
331 self.
_dict[k] = value(__name=name, __at=at, __label=label)
333 self.
_dict[k] = dtype(__name=name, __at=at, __label=label, **value._storage)
337 oldValue.update(__at=at, __label=label, **value._storage)
341 v._rename(_joinNamePath(name=fullname, index=k))
346 object.__setattr__(self, attr, value)
347 elif attr
in self.__dict__
or attr
in [
357 object.__setattr__(self, attr, value)
360 msg = f
"{_typeStr(self._field)} has no attribute {attr}"
364 """Freeze the config.
366 Invoking this freeze method will create a local copy of the field
367 attribute's typemap. This decouples this instance dict from the
368 underlying objects type map ensuring that and subsequent changes to the
369 typemap will not be reflected in this instance (i.e imports adding
370 additional registry entries).
377 f
"Proxy container for config field {self._field.name} cannot "
378 "be pickled; it should be converted to a built-in container before "
379 "being assigned to other objects or variables."
384 """A configuration field (`~lsst.pex.config.Field` subclass) that allows a
385 user to choose from a set of `~lsst.pex.config.Config` types.
390 Documentation string for the field.
391 typemap : `dict`-like
392 A mapping between keys and `~lsst.pex.config.Config`-types as values.
393 See *Examples* for details.
394 default : `str`, optional
395 The default configuration name.
396 optional : `bool`, optional
397 When `False`, `lsst.pex.config.Config.validate` will fail if the
398 field's value is `None`.
399 multi : `bool`, optional
400 If `True`, the field allows multiple selections. In this case, set the
401 selections by assigning a sequence to the ``names`` attribute of the
404 If `False`, the field allows only a single selection. In this case,
405 set the active config by assigning the config's key from the
406 ``typemap`` to the field's ``name`` attribute (see *Examples*).
407 deprecated : None or `str`, optional
408 A description of why this Field is deprecated, including removal date.
409 If not None, the string is appended to the docstring for this Field.
425 ``ConfigChoiceField`` instances can allow either single selections or
426 multiple selections, depending on the ``multi`` parameter. For
427 single-selection fields, set the selection with the ``name`` attribute.
428 For multi-selection fields, set the selection though the ``names``
431 This field is validated only against the active selection. If the
432 ``active`` attribute is `None` and the field is not optional, validation
435 When saving a configuration with a ``ConfigChoiceField``, the entire set is
436 saved, as well as the active selection.
440 While the ``typemap`` is shared by all instances of the field, each
441 instance of the field has its own instance of a particular sub-config type.
443 For example, ``AaaConfig`` is a config object
445 >>> from lsst.pex.config import Config, ConfigChoiceField, Field
446 >>> class AaaConfig(Config):
447 ... somefield = Field("doc", int)
450 The ``MyConfig`` config has a ``ConfigChoiceField`` field called ``choice``
451 that maps the ``AaaConfig`` type to the ``"AAA"`` key:
453 >>> TYPEMAP = {"AAA", AaaConfig}
454 >>> class MyConfig(Config):
455 ... choice = ConfigChoiceField("doc for choice", TYPEMAP)
458 Creating an instance of ``MyConfig``:
460 >>> instance = MyConfig()
462 Setting value of the field ``somefield`` on the "AAA" key of the ``choice``
465 >>> instance.choice['AAA'].somefield = 5
467 **Selecting the active configuration**
469 Make the ``"AAA"`` key the active configuration value for the ``choice``
472 >>> instance.choice = "AAA"
474 Alternatively, the last line can be written:
476 >>> instance.choice.name = "AAA"
478 (If the config instance allows multiple selections, you'd assign a sequence
479 to the ``names`` attribute instead.)
481 ``ConfigChoiceField`` instances also allow multiple values of the same
484 >>> TYPEMAP["CCC"] = AaaConfig
485 >>> TYPEMAP["BBB"] = AaaConfig
488 instanceDictClass = ConfigInstanceDict
490 def __init__(self, doc, typemap, default=None, optional=False, multi=False, deprecated=None):
491 source = getStackFrame()
499 deprecated=deprecated,
505 raise ValueError(
"ConfigChoiceField does not support typing argument")
509 if instanceDict
is None:
511 instanceDict = self.
dtype(instance, self)
512 instanceDict.__doc__ = self.
doc
515 history.append((
"Initialized from defaults", at, label))
521 self, instance: None, owner: Any =
None, at: Any =
None, label: str =
"default"
522 ) -> ConfigChoiceField: ...
526 self, instance: Config, owner: Any =
None, at: Any =
None, label: str =
"default"
527 ) -> ConfigInstanceDict: ...
529 def __get__(self, instance, owner=None, at=None, label="default"):
530 if instance
is None or not isinstance(instance, Config):
536 self, instance: Config, value: ConfigInstanceDict |
None, at: Any =
None, label: str =
"assignment"
544 for k, v
in value.items():
545 instanceDict.__setitem__(k, v, at=at, label=label)
546 instanceDict._setSelection(value._selection, at=at, label=label)
549 instanceDict._setSelection(value, at=at, label=label)
554 instanceDict._rename(fullname)
558 if instanceDict.active
is None and not self.
optional:
559 msg =
"Required field cannot be None"
561 elif instanceDict.active
is not None:
563 for a
in instanceDict.active:
566 instanceDict.active.validate()
573 dict_[
"names"] = instanceDict.names
575 dict_[
"name"] = instanceDict.name
578 for k, v
in instanceDict.items():
579 values[k] = v.toDict()
580 dict_[
"values"] = values
586 instanceDict.freeze()
587 for v
in instanceDict.values():
592 for config
in instanceDict.values():
593 config._collectImports()
594 imports |= config._imports
596 def save(self, outfile, instance):
599 for v
in instanceDict.values():
602 outfile.write(f
"{fullname}.names={sorted(instanceDict.names)!r}\n")
604 outfile.write(f
"{fullname}.name={instanceDict.name!r}\n")
607 """Customize deep-copying, because we always want a reference to the
610 WARNING: this must be overridden by subclasses if they change the
611 constructor signature!
616 default=copy.deepcopy(self.
default),
620 other.source = self.
source
623 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
624 """Compare two fields for equality.
626 Used by `lsst.pex.ConfigChoiceField.compare`.
630 instance1 : `lsst.pex.config.Config`
631 Left-hand side config instance to compare.
632 instance2 : `lsst.pex.config.Config`
633 Right-hand side config instance to compare.
635 If `True`, this function returns as soon as an inequality if found.
637 Relative tolerance for floating point comparisons.
639 Absolute tolerance for floating point comparisons.
641 A callable that takes a string, used (possibly repeatedly) to
647 `True` if the fields are equal, `False` otherwise.
651 Only the selected configurations are compared, as the parameters of any
652 others do not matter.
654 Floating point comparisons are performed by `numpy.allclose`.
658 name = getComparisonName(
661 if not compareScalars(
"selection for %s" % name, d1._selection, d2._selection, output=output):
663 if d1._selection
is None:
666 nested = [(k, d1[k], d2[k])
for k
in d1._selection]
668 nested = [(d1._selection, d1[d1._selection], d2[d1._selection])]
670 for k, c1, c2
in nested:
671 result = compareConfigs(
672 f
"{name}[{k!r}]", c1, c2, shortcut=shortcut, rtol=rtol, atol=atol, output=output
674 if not result
and shortcut:
676 equal = equal
and result
std::vector< SchemaItem< Flag > > * items
__get__(self, instance, owner=None, at=None, label="default")
FieldTypeVar __get__(self, Config instance, Any owner=None, Any at=None, str label="default")
Field[FieldTypeVar] __get__(self, None instance, Any owner=None, Any at=None, str label="default")
_setup(self, doc, dtype, default, check, optional, source, deprecated)
__init__(self, doc, typemap, default=None, optional=False, multi=False, deprecated=None)
_getOrMake(self, instance, label="default")
ConfigInstanceDict __get__(self, Config instance, Any owner=None, Any at=None, str label="default")
__get__(self, instance, owner=None, at=None, label="default")
_collectImports(self, instance, imports)
__class_getitem__(cls, tuple[type,...]|type|ForwardRef params)
_compare(self, instance1, instance2, shortcut, rtol, atol, output)
None __set__(self, Config instance, ConfigInstanceDict|None value, Any at=None, str label="assignment")
save(self, outfile, instance)
ConfigChoiceField __get__(self, None instance, Any owner=None, Any at=None, str label="default")
__setattr__(self, attr, value, at=None, label="assignment")
__setitem__(self, k, value, at=None, label="assignment")
__init__(self, config, field)
__getitem__(self, k, at=None, label="default")
_setSelection(self, value, at=None, label="assignment")
__contains__(self, value)
discard(self, value, at=None)
__init__(self, dict_, value, at=None, label="assignment", setHistory=True)
add(self, value, at=None)
daf::base::PropertySet * set