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.keytype)
56 msg =
"Key %r is of type %s, expected type %s" % \
57 (k, _typeStr(k), _typeStr(self.
_field.keytype))
61 dtype = self.
_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.itemtype))
70 oldValue = self.
_dict.get(k,
None)
73 self.
_dict[k] = dtype(__name=name, __at=at, __label=label)
75 self.
_dict[k] = dtype(__name=name, __at=at, __label=label, **x._storage)
81 oldValue.update(__at=at, __label=label, **x._storage)
83 self.
history.
append((
"Modified item at key %s" % k, at, label))
88 Dict.__delitem__(self, k, at, label,
False)
89 self.
history.
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(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__(instance)
173 if configDict
is not None:
175 fullname = _joinNamePath(instance._name, self.name, k)
176 configDict[k]._rename(fullname)
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__(instance)
191 if configDict
is None:
196 dict_[k] = configDict[k].
toDict()
200 def save(self, outfile, instance):
201 configDict = self.
__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__(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 format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
def compareConfigs(name, c1, c2, shortcut=True, rtol=1E-8, atol=1E-8, output=None)
def __init__(self, doc, keytype, itemtype, default=None, optional=False, dictCheck=None, itemCheck=None, deprecated=None)
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
daf::base::PropertySet * set
def validate(self, instance)
def rename(self, instance)
def getStackFrame(relative=0)
def save(self, outfile, instance)
def freeze(self, instance)
def __get__(self, instance, owner=None, at=None, label="default")
def toDict(self, instance)
def __delitem__(self, k, at=None, label="delitem")
def __init__(self, config, field, value, at, label)
def getComparisonName(name1, name2)
def compareScalars(name, v1, v2, output, rtol=1E-8, atol=1E-8, dtype=None)
def _setup(self, doc, dtype, default, check, optional, source, deprecated)
def __setitem__(self, k, x, at=None, label="setitem", setHistory=True)