24 from .config
import Config, FieldValidationError, _typeStr, _joinNamePath
25 from .dictField
import Dict, DictField
26 from .comparison
import compareConfigs, compareScalars, getComparisonName
28 __all__=[
"ConfigDictField"]
32 Config-Insternal representation of a dict of config classes
34 Much like Dict, ConfigDict is a custom MutableMapper which tracks the
35 history of changes to any of its items.
37 def __init__(self, config, field, value, at, label):
38 Dict.__init__(self, config, field, value, at, label, setHistory=
False)
39 self.history.append((
"Dict initialized", at, label))
41 def __setitem__(self, k, x, at=None, label="setitem", setHistory=True):
42 if self._config._frozen:
43 msg =
"Cannot modify a frozen Config. "\
44 "Attempting to set item at key %r to value %s"%(k, x)
45 raise FieldValidationError(self._field, self._config, msg)
48 if type(k) != self._field.keytype:
49 msg =
"Key %r is of type %s, expected type %s"%\
51 raise FieldValidationError(self._field, self._config, msg)
54 dtype = self._field.itemtype
55 if type(x) != self._field.itemtype
and x != self._field.itemtype:
56 msg =
"Value %s at key %r is of incorrect type %s. Expected type %s"%\
58 raise FieldValidationError(self._field, self._config, msg)
61 at = traceback.extract_stack()[:-1]
63 oldValue = self._dict.get(k,
None)
66 self._dict[k] = dtype(__name=name, __at=at, __label=label)
68 self._dict[k] = dtype(__name=name, __at=at, __label=label, **x._storage)
70 self.history.append((
"Added item at key %s"%k, at, label))
74 oldValue.update(__at=at, __label=label, **value._storage)
76 self.history.append((
"Modified item at key %s"%k, at, label))
81 at = traceback.extract_stack()[:-1]
82 Dict.__delitem__(self, k, at, label,
False)
83 self.history.append((
"Removed item at key %s"%k, at, label))
87 Defines a field which is a mapping between a POD and a config class.
89 This behaves exactly like a DictField with the slight difference that
90 itemtype must be an subclass of Config.
92 This allows config writters to create name-to-config mappings. One use case
93 is for configuring mappings for dataset types in a butler. In this case,
94 the dataset type names are arbitrary and user-selected; the mapping
95 configurations are known and fixed.
98 DictClass = ConfigDict
99 def __init__(self, doc, keytype, itemtype, default=None, optional=False, dictCheck=None, itemCheck=None):
100 source = traceback.extract_stack(limit=2)[0]
101 self._setup( doc=doc, dtype=ConfigDict, default=default, check=
None,
102 optional=optional, source=source)
103 if keytype
not in self.supportedTypes:
104 raise ValueError(
"'keytype' %s is not a supported type"%\
106 elif not issubclass(itemtype, Config):
107 raise ValueError(
"'itemtype' %s is not a supported type"%\
109 if dictCheck
is not None and not hasattr(dictCheck,
"__call__"):
110 raise ValueError(
"'dictCheck' must be callable")
111 if itemCheck
is not None and not hasattr(itemCheck,
"__call__"):
112 raise ValueError(
"'itemCheck' must be callable")
120 configDict = self.__get__(instance)
121 if configDict
is not None:
124 configDict[k]._rename(fullname)
128 value = self.__get__(instance)
129 if value
is not None:
134 msg=
"Item at key %r is not a valid value: %s"%(k, item)
135 raise FieldValidationError(self, instance, msg)
136 DictField.validate(self, instance)
139 configDict = self.__get__(instance)
140 if configDict
is None:
145 dict_[k]= configDict[k].
toDict()
149 def save(self, outfile, instance):
150 configDict = self.__get__(instance)
152 if configDict
is None:
153 print >>outfile,
"%s=%r"%(fullname, configDict)
156 print >>outfile,
"%s=%r"%(fullname, {})
157 for v
in configDict.itervalues():
158 print >>outfile,
"%s=%s()"%(v._name,
_typeStr(v))
162 configDict = self.__get__(instance)
163 if configDict
is not None:
167 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
168 """Helper function for Config.compare; used to compare two fields for equality.
170 @param[in] instance1 LHS Config instance to compare.
171 @param[in] instance2 RHS Config instance to compare.
172 @param[in] shortcut If True, return as soon as an inequality is found.
173 @param[in] rtol Relative tolerance for floating point comparisons.
174 @param[in] atol Absolute tolerance for floating point comparisons.
175 @param[in] output If not None, a callable that takes a string, used (possibly repeatedly)
176 to report inequalities.
178 Floating point comparisons are performed by numpy.allclose; refer to that for details.
180 d1 = getattr(instance1, self.name)
181 d2 = getattr(instance2, self.name)
186 if not compareScalars(
"keys for %s" % name, d1.keys(), d2.keys(), output=output):
189 for k, v1
in d1.iteritems():
191 result =
compareConfigs(
"%s[%r]" % (name, k), v1, v2, shortcut=shortcut,
192 rtol=rtol, atol=atol, output=output)
193 if not result
and shortcut:
195 equal = equal
and result