23 from .config
import Config, FieldValidationError, _autocast, _typeStr, _joinNamePath
24 from .dictField
import Dict, DictField
25 from .comparison
import compareConfigs, compareScalars, getComparisonName
26 from .callStack
import getCallStack, getStackFrame
28 __all__ = [
"ConfigDictField"]
32 """Internal representation of a dictionary of configuration classes. 34 Much like `Dict`, `ConfigDict` is a custom `MutableMapper` which tracks 35 the history of changes to any of its items. 38 def __init__(self, config, field, value, at, label):
39 Dict.__init__(self, config, field, value, at, label, setHistory=
False)
42 def __setitem__(self, k, x, at=None, label="setitem", setHistory=True):
44 msg =
"Cannot modify a frozen Config. "\
45 "Attempting to set item at key %r to value %s" % (k, x)
49 k = _autocast(k, self.
_field.keytype)
51 msg =
"Key %r is of type %s, expected type %s" % \
52 (k, _typeStr(k), _typeStr(self.
_field.keytype))
56 dtype = self.
_field.itemtype
58 msg =
"Value %s at key %r is of incorrect type %s. Expected type %s" % \
59 (x, k, _typeStr(x), _typeStr(self.
_field.itemtype))
65 oldValue = self.
_dict.get(k,
None)
68 self.
_dict[k] = dtype(__name=name, __at=at, __label=label)
70 self.
_dict[k] = dtype(__name=name, __at=at, __label=label, **x._storage)
76 oldValue.update(__at=at, __label=label, **x._storage)
78 self.
history.
append((
"Modified item at key %s" % k, at, label))
83 Dict.__delitem__(self, k, at, label,
False)
84 self.
history.
append((
"Removed item at key %s" % k, at, label))
88 """A configuration field (`~lsst.pex.config.Field` subclass) that is a 89 mapping of keys to `~lsst.pex.config.Config` instances. 91 ``ConfigDictField`` behaves like `DictField` except that the 92 ``itemtype`` must be a `~lsst.pex.config.Config` subclass. 97 A description of the configuration field. 98 keytype : {`int`, `float`, `complex`, `bool`, `str`} 99 The type of the mapping keys. All keys must have this type. 100 itemtype : `lsst.pex.config.Config`-type 101 The type of the values in the mapping. This must be 102 `~lsst.pex.config.Config` or a subclass. 105 default : ``itemtype``-dtype, optional 106 Default value of this field. 107 optional : `bool`, optional 108 If `True`, this configuration `~lsst.pex.config.Field` is *optional*. 114 Raised if the inputs are invalid: 116 - ``keytype`` or ``itemtype`` arguments are not supported types 117 (members of `ConfigDictField.supportedTypes`. 118 - ``dictCheck`` or ``itemCheck`` is not a callable function. 134 You can use ``ConfigDictField`` to create name-to-config mappings. One use 135 case is for configuring mappings for dataset types in a Butler. In this 136 case, the dataset type names are arbitrary and user-selected while the 137 mapping configurations are known and fixed. 140 DictClass = ConfigDict
142 def __init__(self, doc, keytype, itemtype, default=None, optional=False, dictCheck=None, itemCheck=None):
144 self.
_setup(doc=doc, dtype=ConfigDict, default=default, check=
None,
145 optional=optional, source=source)
147 raise ValueError(
"'keytype' %s is not a supported type" %
149 elif not issubclass(itemtype, Config):
150 raise ValueError(
"'itemtype' %s is not a supported type" %
152 if dictCheck
is not None and not hasattr(dictCheck,
"__call__"):
153 raise ValueError(
"'dictCheck' must be callable")
154 if itemCheck
is not None and not hasattr(itemCheck,
"__call__"):
155 raise ValueError(
"'itemCheck' must be callable")
163 configDict = self.
__get__(instance)
164 if configDict
is not None:
166 fullname = _joinNamePath(instance._name, self.name, k)
167 configDict[k]._rename(fullname)
171 if value
is not None:
176 msg =
"Item at key %r is not a valid value: %s" % (k, item)
178 DictField.validate(self, instance)
181 configDict = self.
__get__(instance)
182 if configDict
is None:
187 dict_[k] = configDict[k].
toDict()
191 def save(self, outfile, instance):
192 configDict = self.
__get__(instance)
193 fullname = _joinNamePath(instance._name, self.name)
194 if configDict
is None:
195 outfile.write(
u"{}={!r}\n".
format(fullname, configDict))
198 outfile.write(
u"{}={!r}\n".
format(fullname, {}))
199 for v
in configDict.values():
200 outfile.write(
u"{}={}()\n".
format(v._name, _typeStr(v)))
204 configDict = self.
__get__(instance)
205 if configDict
is not None:
209 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
210 """Compare two fields for equality. 212 Used by `lsst.pex.ConfigDictField.compare`. 216 instance1 : `lsst.pex.config.Config` 217 Left-hand side config instance to compare. 218 instance2 : `lsst.pex.config.Config` 219 Right-hand side config instance to compare. 221 If `True`, this function returns as soon as an inequality if found. 223 Relative tolerance for floating point comparisons. 225 Absolute tolerance for floating point comparisons. 227 A callable that takes a string, used (possibly repeatedly) to report inequalities. 232 `True` if the fields are equal, `False` otherwise. 236 Floating point comparisons are performed by `numpy.allclose`. 238 d1 = getattr(instance1, self.name)
239 d2 = getattr(instance2, self.name)
241 _joinNamePath(instance1._name, self.name),
242 _joinNamePath(instance2._name, self.name)
247 for k, v1
in d1.items():
249 result =
compareConfigs(
"%s[%r]" % (name, k), v1, v2, shortcut=shortcut,
250 rtol=rtol, atol=atol, output=output)
251 if not result
and shortcut:
253 equal = equal
and result
def __init__(self, doc, keytype, itemtype, default=None, optional=False, dictCheck=None, itemCheck=None)
def __setitem__(self, k, x, at=None, label="setitem", setHistory=True)
def toDict(self, instance)
def save(self, outfile, instance)
def compareConfigs(name, c1, c2, shortcut=True, rtol=1E-8, atol=1E-8, output=None)
def validate(self, instance)
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 freeze(self, instance)
def __get__(self, instance, owner=None, at=None, label="default")
def _setup(self, doc, dtype, default, check, optional, source)
def rename(self, instance)
def getStackFrame(relative=0)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
def __delitem__(self, k, at=None, label="delitem")
def compareScalars(name, v1, v2, output, rtol=1E-8, atol=1E-8, dtype=None)
def getComparisonName(name1, name2)
def __init__(self, config, field, value, at, label)