22 import traceback, copy
25 from .config
import Config, Field, FieldValidationError, _typeStr, _autocast, _joinNamePath
26 from .dictField
import Dict, DictField
27 from .comparison
import *
29 __all__=[
"ConfigDictField"]
33 Config-Insternal representation of a dict of config classes
35 Much like Dict, ConfigDict is a custom MutableMapper which tracks the
36 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)
40 self.history.append((
"Dict initialized", at, label))
42 def __setitem__(self, k, x, at=None, label="setitem", setHistory=True):
43 if self._config._frozen:
44 msg =
"Cannot modify a frozen Config. "\
45 "Attempting to set item at key %r to value %s"%(k, x)
46 raise FieldValidationError(self._field, self._config, msg)
49 if type(k) != self._field.keytype:
50 msg =
"Key %r is of type %s, expected type %s"%\
52 raise FieldValidationError(self._field, self._config, msg)
55 dtype = self._field.itemtype
56 if type(x) != self._field.itemtype
and x != self._field.itemtype:
57 msg =
"Value %s at key %r is of incorrect type %s. Expected type %s"%\
59 raise FieldValidationError(self._field, self._config, msg)
62 at = traceback.extract_stack()[:-1]
64 oldValue = self._dict.get(k,
None)
67 self._dict[k] = dtype(__name=name, __at=at, __label=label)
69 self._dict[k] = dtype(__name=name, __at=at, __label=label, **x._storage)
71 self.history.append((
"Added item at key %s"%k, at, label))
75 oldValue.update(__at=at, __label=label, **value._storage)
77 self.history.append((
"Modified item at key %s"%k, at, label))
82 at = traceback.extract_stack()[:-1]
83 Dict.__delitem__(self, k, at, label,
False)
84 self.history.append((
"Removed item at key %s"%k, at, label))
88 Defines a field which is a mapping between a POD and a config class.
90 This behaves exactly like a DictField with the slight difference that
91 itemtype must be an subclass of Config.
93 This allows config writters to create name-to-config mappings. One use case
94 is for configuring mappings for dataset types in a butler. In this case,
95 the dataset type names are arbitrary and user-selected; the mapping
96 configurations are known and fixed.
99 DictClass = ConfigDict
100 def __init__(self, doc, keytype, itemtype, default=None, optional=False, dictCheck=None, itemCheck=None):
101 source = traceback.extract_stack(limit=2)[0]
102 self._setup( doc=doc, dtype=ConfigDict, default=default, check=
None,
103 optional=optional, source=source)
104 if keytype
not in self.supportedTypes:
105 raise ValueError(
"'keytype' %s is not a supported type"%\
107 elif not issubclass(itemtype, Config):
108 raise ValueError(
"'itemtype' %s is not a supported type"%\
110 if dictCheck
is not None and not hasattr(dictCheck,
"__call__"):
111 raise ValueError(
"'dictCheck' must be callable")
112 if itemCheck
is not None and not hasattr(itemCheck,
"__call__"):
113 raise ValueError(
"'itemCheck' must be callable")
121 configDict = self.__get__(instance)
122 if configDict
is not None:
125 configDict[k]._rename(fullname)
129 value = self.__get__(instance)
130 if value
is not None:
135 msg=
"Item at key %r is not a valid value: %s"%(k, item)
136 raise FieldValidationError(self, instance, msg)
137 DictField.validate(self, instance)
140 configDict = self.__get__(instance)
141 if configDict
is None:
146 dict_[k]= configDict[k].
toDict()
150 def save(self, outfile, instance):
151 configDict = self.__get__(instance)
153 if configDict
is None:
154 print >>outfile,
"%s=%r"%(fullname, configDict)
157 print >>outfile,
"%s=%r"%(fullname, {})
158 for v
in configDict.itervalues():
159 print >>outfile,
"%s=%s()"%(v._name,
_typeStr(v))
163 configDict = self.__get__(instance)
164 if configDict
is not None:
168 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
169 """Helper function for Config.compare; used to compare two fields for equality.
171 @param[in] instance1 LHS Config instance to compare.
172 @param[in] instance2 RHS Config instance to compare.
173 @param[in] shortcut If True, return as soon as an inequality is found.
174 @param[in] rtol Relative tolerance for floating point comparisons.
175 @param[in] atol Absolute tolerance for floating point comparisons.
176 @param[in] output If not None, a callable that takes a string, used (possibly repeatedly)
177 to report inequalities.
179 Floating point comparisons are performed by numpy.allclose; refer to that for details.
181 d1 = getattr(instance1, self.name)
182 d2 = getattr(instance2, self.name)
187 if not compareScalars(
"keys for %s" % name, d1.keys(), d2.keys(), output=output):
190 for k, v1
in d1.iteritems():
192 result =
compareConfigs(
"%s[%r]" % (name, k), v1, v2, shortcut=shortcut,
193 rtol=rtol, atol=atol, output=output)
194 if not result
and shortcut:
196 equal = equal
and result