22 from __future__
import print_function
24 from .config
import Config, FieldValidationError, _autocast, _typeStr, _joinNamePath
25 from .dictField
import Dict, DictField
26 from .comparison
import compareConfigs, compareScalars, getComparisonName
27 from .callStack
import getCallStack, getStackFrame
29 __all__ = [
"ConfigDictField"]
34 Config-Insternal representation of a dict of config classes 36 Much like Dict, ConfigDict is a custom MutableMapper which tracks the 37 history of changes to any of its items. 39 def __init__(self, config, field, value, at, label):
40 Dict.__init__(self, config, field, value, at, label, setHistory=
False)
41 self.
history.append((
"Dict initialized", at, label))
43 def __setitem__(self, k, x, at=None, label="setitem", setHistory=True):
45 msg =
"Cannot modify a frozen Config. "\
46 "Attempting to set item at key %r to value %s" % (k, x)
50 k = _autocast(k, self.
_field.keytype)
51 if type(k) != self.
_field.keytype:
52 msg =
"Key %r is of type %s, expected type %s" % \
53 (k, _typeStr(k), _typeStr(self.
_field.keytype))
57 dtype = self.
_field.itemtype
58 if type(x) != self.
_field.itemtype
and x != self.
_field.itemtype:
59 msg =
"Value %s at key %r is of incorrect type %s. Expected type %s" % \
60 (x, k, _typeStr(x), _typeStr(self.
_field.itemtype))
66 oldValue = self.
_dict.get(k,
None)
69 self.
_dict[k] = dtype(__name=name, __at=at, __label=label)
71 self.
_dict[k] = dtype(__name=name, __at=at, __label=label, **x._storage)
73 self.
history.append((
"Added item at key %s" % k, at, label))
77 oldValue.update(__at=at, __label=label, **x._storage)
79 self.
history.append((
"Modified item at key %s" % k, at, label))
84 Dict.__delitem__(self, k, at, label,
False)
85 self.
history.append((
"Removed item at key %s" % k, at, label))
90 Defines a field which is a mapping between a POD and a config class. 92 This behaves exactly like a DictField with the slight difference that 93 itemtype must be an subclass of Config. 95 This allows config writters to create name-to-config mappings. One use case 96 is for configuring mappings for dataset types in a butler. In this case, 97 the dataset type names are arbitrary and user-selected; the mapping 98 configurations are known and fixed. 101 DictClass = ConfigDict
103 def __init__(self, doc, keytype, itemtype, default=None, optional=False, dictCheck=None, itemCheck=None):
105 self.
_setup(doc=doc, dtype=ConfigDict, default=default, check=
None,
106 optional=optional, source=source)
108 raise ValueError(
"'keytype' %s is not a supported type" %
110 elif not issubclass(itemtype, Config):
111 raise ValueError(
"'itemtype' %s is not a supported type" %
113 if dictCheck
is not None and not hasattr(dictCheck,
"__call__"):
114 raise ValueError(
"'dictCheck' must be callable")
115 if itemCheck
is not None and not hasattr(itemCheck,
"__call__"):
116 raise ValueError(
"'itemCheck' must be callable")
124 configDict = self.
__get__(instance)
125 if configDict
is not None:
127 fullname = _joinNamePath(instance._name, self.name, k)
128 configDict[k]._rename(fullname)
132 if value
is not None:
137 msg =
"Item at key %r is not a valid value: %s" % (k, item)
139 DictField.validate(self, instance)
142 configDict = self.
__get__(instance)
143 if configDict
is None:
148 dict_[k] = configDict[k].
toDict()
152 def save(self, outfile, instance):
153 configDict = self.
__get__(instance)
154 fullname = _joinNamePath(instance._name, self.name)
155 if configDict
is None:
156 outfile.write(
u"{}={!r}\n".
format(fullname, configDict))
159 outfile.write(
u"{}={!r}\n".
format(fullname, {}))
160 for v
in configDict.values():
161 outfile.write(
u"{}={}()\n".
format(v._name, _typeStr(v)))
165 configDict = self.
__get__(instance)
166 if configDict
is not None:
170 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
171 """Helper function for Config.compare; used to compare two fields for equality. 173 @param[in] instance1 LHS Config instance to compare. 174 @param[in] instance2 RHS Config instance to compare. 175 @param[in] shortcut If True, return as soon as an inequality is found. 176 @param[in] rtol Relative tolerance for floating point comparisons. 177 @param[in] atol Absolute tolerance for floating point comparisons. 178 @param[in] output If not None, a callable that takes a string, used (possibly repeatedly) 179 to report inequalities. 181 Floating point comparisons are performed by numpy.allclose; refer to that for details. 183 d1 = getattr(instance1, self.name)
184 d2 = getattr(instance2, self.name)
186 _joinNamePath(instance1._name, self.name),
187 _joinNamePath(instance2._name, self.name)
189 if not compareScalars(
"keys for %s" % name, set(d1.keys()), set(d2.keys()), output=output):
192 for k, v1
in d1.items():
194 result =
compareConfigs(
"%s[%r]" % (name, k), v1, v2, shortcut=shortcut,
195 rtol=rtol, atol=atol, output=output)
196 if not result
and shortcut:
198 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)
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)