28 __all__ = [
"ConfigDictField"]
30 from .config
import Config, FieldValidationError, _autocast, _typeStr, _joinNamePath
31 from .dictField
import Dict, DictField
32 from .comparison
import compareConfigs, compareScalars, getComparisonName
33 from .callStack
import getCallStack, getStackFrame
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. "\
50 "Attempting to set item at key %r to value %s" % (k, x)
54 k = _autocast(k, self.
_field_field.keytype)
56 msg =
"Key %r is of type %s, expected type %s" % \
57 (k, _typeStr(k), _typeStr(self.
_field_field.keytype))
61 dtype = self.
_field_field.itemtype
62 if type(x) != self.
_field_field.itemtype
and x != self.
_field_field.itemtype:
63 msg =
"Value %s at key %r is of incorrect type %s. Expected type %s" % \
64 (x, k, _typeStr(x), _typeStr(self.
_field_field.itemtype))
69 name = _joinNamePath(self.
_config_config._name, self.
_field_field.name, k)
70 oldValue = self.
_dict_dict.get(k,
None)
73 self.
_dict_dict[k] =
dtype(__name=name, __at=at, __label=label)
75 self.
_dict_dict[k] =
dtype(__name=name, __at=at, __label=label, **x._storage)
77 self.
historyhistory.
append((
"Added item at key %s" % k, at, label))
81 oldValue.update(__at=at, __label=label, **x._storage)
83 self.
historyhistory.
append((
"Modified item at key %s" % k, at, label))
88 Dict.__delitem__(self, k, at, label,
False)
89 self.
historyhistory.
append((
"Removed item at key %s" % k, at, label))
93 """A configuration field (`~lsst.pex.config.Field` subclass) that is a
94 mapping of keys to `~lsst.pex.config.Config` instances.
96 ``ConfigDictField`` behaves like `DictField` except that the
97 ``itemtype`` must be a `~lsst.pex.config.Config` subclass.
102 A description of the configuration field.
103 keytype : {`int`, `float`, `complex`, `bool`, `str`}
104 The type of the mapping keys. All keys must have this type.
105 itemtype : `lsst.pex.config.Config`-type
106 The type of the values in the mapping. This must be
107 `~lsst.pex.config.Config` or a subclass.
110 default : ``itemtype``-dtype, optional
111 Default value of this field.
112 optional : `bool`, optional
113 If `True`, this configuration `~lsst.pex.config.Field` is *optional*.
115 deprecated : None or `str`, optional
116 A description of why this Field is deprecated, including removal date.
117 If not None, the string is appended to the docstring for this Field.
122 Raised if the inputs are invalid:
124 - ``keytype`` or ``itemtype`` arguments are not supported types
125 (members of `ConfigDictField.supportedTypes`.
126 - ``dictCheck`` or ``itemCheck`` is not a callable function.
142 You can use ``ConfigDictField`` to create name-to-config mappings. One use
143 case is for configuring mappings for dataset types in a Butler. In this
144 case, the dataset type names are arbitrary and user-selected while the
145 mapping configurations are known and fixed.
148 DictClass = ConfigDict
150 def __init__(self, doc, keytype, itemtype, default=None, optional=False, dictCheck=None, itemCheck=None,
153 self.
_setup_setup(doc=doc, dtype=ConfigDict, default=default, check=
None,
154 optional=optional, source=source, deprecated=deprecated)
156 raise ValueError(
"'keytype' %s is not a supported type" %
158 elif not issubclass(itemtype, Config):
159 raise ValueError(
"'itemtype' %s is not a supported type" %
161 if dictCheck
is not None and not hasattr(dictCheck,
"__call__"):
162 raise ValueError(
"'dictCheck' must be callable")
163 if itemCheck
is not None and not hasattr(itemCheck,
"__call__"):
164 raise ValueError(
"'itemCheck' must be callable")
172 configDict = self.
__get____get__(instance)
173 if configDict
is not None:
175 fullname = _joinNamePath(instance._name, self.name, k)
176 configDict[k]._rename(fullname)
179 value = self.
__get____get__(instance)
180 if value
is not None:
185 msg =
"Item at key %r is not a valid value: %s" % (k, item)
187 DictField.validate(self, instance)
190 configDict = self.
__get____get__(instance)
191 if configDict
is None:
196 dict_[k] = configDict[k].
toDict()
200 def save(self, outfile, instance):
201 configDict = self.
__get____get__(instance)
202 fullname = _joinNamePath(instance._name, self.name)
203 if configDict
is None:
204 outfile.write(
u"{}={!r}\n".
format(fullname, configDict))
207 outfile.write(
u"{}={!r}\n".
format(fullname, {}))
208 for v
in configDict.values():
209 outfile.write(
u"{}={}()\n".
format(v._name, _typeStr(v)))
213 configDict = self.
__get____get__(instance)
214 if configDict
is not None:
218 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
219 """Compare two fields for equality.
221 Used by `lsst.pex.ConfigDictField.compare`.
225 instance1 : `lsst.pex.config.Config`
226 Left-hand side config instance to compare.
227 instance2 : `lsst.pex.config.Config`
228 Right-hand side config instance to compare.
230 If `True`, this function returns as soon as an inequality if found.
232 Relative tolerance for floating point comparisons.
234 Absolute tolerance for floating point comparisons.
236 A callable that takes a string, used (possibly repeatedly) to
242 `True` if the fields are equal, `False` otherwise.
246 Floating point comparisons are performed by `numpy.allclose`.
248 d1 = getattr(instance1, self.name)
249 d2 = getattr(instance2, self.name)
251 _joinNamePath(instance1._name, self.name),
252 _joinNamePath(instance2._name, self.name)
257 for k, v1
in d1.items():
259 result =
compareConfigs(
"%s[%r]" % (name, k), v1, v2, shortcut=shortcut,
260 rtol=rtol, atol=atol, output=output)
261 if not result
and shortcut:
263 equal = equal
and result
def __get__(self, instance, owner=None, at=None, label="default")
def _setup(self, doc, dtype, default, check, optional, source, deprecated)
def validate(self, instance)
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
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
def getStackFrame(relative=0)
def compareConfigs(name, c1, c2, shortcut=True, rtol=1E-8, atol=1E-8, output=None)
def compareScalars(name, v1, v2, output, rtol=1E-8, atol=1E-8, dtype=None)
def getComparisonName(name1, name2)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)