28__all__ = [
"ConfigDictField"]
30from .callStack
import getCallStack, getStackFrame
31from .comparison
import compareConfigs, compareScalars, getComparisonName
32from .config
import Config, FieldValidationError, _autocast, _joinNamePath, _typeStr
33from .dictField
import Dict, DictField
37 """Internal representation of a dictionary of configuration classes.
39 Much like `Dict`, `ConfigDict` is a custom `MutableMapper` which tracks
40 the history of changes to any of its items.
43 def __init__(self, config, field, value, at, label):
44 Dict.__init__(self, config, field, value, at, label, setHistory=
False)
47 def __setitem__(self, k, x, at=None, label="setitem", setHistory=True):
49 msg =
"Cannot modify a frozen Config. Attempting to set item at key %r to value %s" % (k, x)
53 k = _autocast(k, self.
_field.keytype)
55 msg =
"Key %r is of type %s, expected type %s" % (k, _typeStr(k), _typeStr(self.
_field.keytype))
59 dtype = self.
_field.itemtype
61 msg =
"Value %s at key %r is of incorrect type %s. Expected type %s" % (
65 _typeStr(self.
_field.itemtype),
72 oldValue = self.
_dict.get(k,
None)
75 self.
_dict[k] = dtype(__name=name, __at=at, __label=label)
77 self.
_dict[k] = dtype(__name=name, __at=at, __label=label, **x._storage)
79 self.
historyhistory.append((
"Added item at key %s" % k, at, label))
83 oldValue.update(__at=at, __label=label, **x._storage)
85 self.
historyhistory.append((
"Modified item at key %s" % k, at, label))
90 Dict.__delitem__(self, k, at, label,
False)
91 self.
historyhistory.append((
"Removed item at key %s" % k, at, label))
95 """A configuration field (`~lsst.pex.config.Field` subclass) that is a
98 ``ConfigDictField`` behaves like `DictField` except that the
104 A description of the configuration field.
105 keytype : {`int`, `float`, `complex`, `bool`, `str`}
106 The type of the mapping keys. All keys must have this type.
108 The type of the values
in the mapping. This must be
112 default : ``itemtype``-dtype, optional
113 Default value of this field.
114 optional : `bool`, optional
117 deprecated :
None or `str`, optional
118 A description of why this Field
is deprecated, including removal date.
119 If
not None, the string
is appended to the docstring
for this Field.
124 Raised
if the inputs are invalid:
126 - ``keytype``
or ``itemtype`` arguments are
not supported types
127 (members of `ConfigDictField.supportedTypes`.
128 - ``dictCheck``
or ``itemCheck``
is not a callable function.
144 You can use ``ConfigDictField`` to create name-to-config mappings. One use
145 case
is for configuring mappings
for dataset types
in a Butler. In this
146 case, the dataset type names are arbitrary
and user-selected
while the
147 mapping configurations are known
and fixed.
150 DictClass = ConfigDict
163 source = getStackFrame()
171 deprecated=deprecated,
174 raise ValueError(
"'keytype' %s is not a supported type" % _typeStr(keytype))
175 elif not issubclass(itemtype, Config):
176 raise ValueError(
"'itemtype' %s is not a supported type" % _typeStr(itemtype))
177 if dictCheck
is not None and not hasattr(dictCheck,
"__call__"):
178 raise ValueError(
"'dictCheck' must be callable")
179 if itemCheck
is not None and not hasattr(itemCheck,
"__call__"):
180 raise ValueError(
"'itemCheck' must be callable")
189 if configDict
is not None:
191 fullname = _joinNamePath(instance._name, self.name, k)
192 configDict[k]._rename(fullname)
196 if value
is not None:
201 msg =
"Item at key %r is not a valid value: %s" % (k, item)
203 DictField.validate(self, instance)
207 if configDict
is None:
212 dict_[k] = configDict[k].
toDict()
216 def _collectImports(self, instance, imports):
219 if configDict
is not None:
220 for v
in configDict.values():
222 imports |= v._imports
224 def save(self, outfile, instance):
226 fullname = _joinNamePath(instance._name, self.name)
227 if configDict
is None:
228 outfile.write(
"{}={!r}\n".format(fullname, configDict))
231 outfile.write(
"{}={!r}\n".format(fullname, {}))
232 for v
in configDict.values():
233 outfile.write(
"{}={}()\n".format(v._name, _typeStr(v)))
238 if configDict
is not None:
242 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
243 """Compare two fields for equality.
245 Used by `lsst.pex.ConfigDictField.compare`.
250 Left-hand side config instance to compare.
252 Right-hand side config instance to compare.
254 If `True`, this function returns
as soon
as an inequality
if found.
256 Relative tolerance
for floating point comparisons.
258 Absolute tolerance
for floating point comparisons.
260 A callable that takes a string, used (possibly repeatedly) to
266 `
True`
if the fields are equal, `
False` otherwise.
270 Floating point comparisons are performed by `numpy.allclose`.
272 d1 = getattr(instance1, self.name)
273 d2 = getattr(instance2, self.name)
274 name = getComparisonName(
275 _joinNamePath(instance1._name, self.name), _joinNamePath(instance2._name, self.name)
277 if not compareScalars(
"keys for %s" % name,
set(d1.keys()),
set(d2.keys()), output=output):
280 for k, v1
in d1.items():
282 result = compareConfigs(
283 "%s[%r]" % (name, k), v1, v2, shortcut=shortcut, rtol=rtol, atol=atol, output=output
285 if not result
and shortcut:
287 equal = equal
and result
"Field[FieldTypeVar]" __get__(self, None instance, Any owner=None, Any at=None, str label="default")
def __get__(self, instance, owner=None, at=None, label="default")
FieldTypeVar __get__(self, "Config" instance, Any owner=None, Any at=None, str label="default")
def _setup(self, doc, dtype, default, check, optional, source, deprecated)
def rename(self, instance)
def save(self, outfile, instance)
def toDict(self, instance)
def __init__(self, doc, keytype, itemtype, default=None, optional=False, dictCheck=None, itemCheck=None, deprecated=None)
def freeze(self, instance)
def __delitem__(self, k, at=None, label="delitem")
def __setitem__(self, k, x, at=None, label="setitem", setHistory=True)
def __init__(self, config, field, value, at, label)
daf::base::PropertySet * set