22 from builtins
import str
26 from .config
import Field, FieldValidationError, _typeStr, _autocast, _joinNamePath
27 from .comparison
import getComparisonName, compareScalars
28 from .callStack
import getCallStack, getStackFrame
30 __all__ = [
"DictField"]
33 class Dict(collections.MutableMapping):
35 Config-Internal mapping container 36 Emulates a dict, but adds validation and provenance. 39 def __init__(self, config, field, value, at, label, setHistory=True):
49 self.
__setitem__(k, value[k], at=at, label=label, setHistory=
False)
51 msg =
"Value %s is of incorrect type %s. Mapping type expected." % \
52 (value, _typeStr(value))
60 history = property(
lambda x: x._history)
66 return len(self.
_dict)
69 return iter(self.
_dict)
72 return k
in self.
_dict 74 def __setitem__(self, k, x, at=None, label="setitem", setHistory=True):
76 msg =
"Cannot modify a frozen Config. "\
77 "Attempting to set item at key %r to value %s" % (k, x)
81 k = _autocast(k, self.
_field.keytype)
82 if type(k) != self.
_field.keytype:
83 msg =
"Key %r is of type %s, expected type %s" % \
84 (k, _typeStr(k), _typeStr(self.
_field.keytype))
88 x = _autocast(x, self.
_field.itemtype)
89 if self.
_field.itemtype
is None:
90 if type(x)
not in self.
_field.supportedTypes
and x
is not None:
91 msg =
"Value %s at key %r is of invalid type %s" % (x, k, _typeStr(x))
94 if type(x) != self.
_field.itemtype
and x
is not None:
95 msg =
"Value %s at key %r is of incorrect type %s. Expected type %s" % \
96 (x, k, _typeStr(x), _typeStr(self.
_field.itemtype))
100 if self.
_field.itemCheck
is not None and not self.
_field.itemCheck(x):
101 msg =
"Item at key %r is not a valid value: %s" % (k, x)
111 def __delitem__(self, k, at=None, label="delitem", setHistory=True):
114 "Cannot modify a frozen Config")
123 return repr(self.
_dict)
126 return str(self.
_dict)
129 if hasattr(getattr(self.__class__, attr,
None),
'__set__'):
131 object.__setattr__(self, attr, value)
132 elif attr
in self.__dict__
or attr
in [
"_field",
"_config",
"_history",
"_dict",
"__doc__"]:
134 object.__setattr__(self, attr, value)
137 msg =
"%s has no attribute %s" % (_typeStr(self.
_field), attr)
143 Defines a field which is a mapping of values 145 Both key and item types are restricted to builtin POD types: 146 (int, float, complex, bool, str) 148 Users can provide two check functions: 149 dictCheck: used to validate the dict as a whole, and 150 itemCheck: used to validate each item individually 152 For example to define a field which is a mapping from names to int values: 154 class MyConfig(Config): 156 doc="example string-to-int mapping field", 157 keytype=str, itemtype=int, 162 def __init__(self, doc, keytype, itemtype, default=None, optional=False, dictCheck=None, itemCheck=None):
164 self.
_setup(doc=doc, dtype=Dict, default=default, check=
None,
165 optional=optional, source=source)
167 raise ValueError(
"'keytype' %s is not a supported type" %
169 elif itemtype
is not None and itemtype
not in self.
supportedTypes:
170 raise ValueError(
"'itemtype' %s is not a supported type" %
172 if dictCheck
is not None and not hasattr(dictCheck,
"__call__"):
173 raise ValueError(
"'dictCheck' must be callable")
174 if itemCheck
is not None and not hasattr(itemCheck,
"__call__"):
175 raise ValueError(
"'itemCheck' must be callable")
184 DictField validation ensures that non-optional fields are not None, 185 and that non-None values comply with dictCheck. 186 Individual Item checks are applied at set time and are not re-checked. 188 Field.validate(self, instance)
190 if value
is not None and self.
dictCheck is not None \
192 msg =
"%s is not a valid value" % str(value)
195 def __set__(self, instance, value, at=None, label="assignment"):
197 msg =
"Cannot modify a frozen Config. "\
198 "Attempting to set field to value %s" % value
203 if value
is not None:
204 value = self.
DictClass(instance, self, value, at=at, label=label)
206 history = instance._history.setdefault(self.name, [])
207 history.append((value, at, label))
209 instance._storage[self.name] = value
213 return dict(value)
if value
is not None else None 215 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
216 """Helper function for Config.compare; used to compare two fields for equality. 218 @param[in] instance1 LHS Config instance to compare. 219 @param[in] instance2 RHS Config instance to compare. 220 @param[in] shortcut If True, return as soon as an inequality is found. 221 @param[in] rtol Relative tolerance for floating point comparisons. 222 @param[in] atol Absolute tolerance for floating point comparisons. 223 @param[in] output If not None, a callable that takes a string, used (possibly repeatedly) 224 to report inequalities. 226 Floating point comparisons are performed by numpy.allclose; refer to that for details. 228 d1 = getattr(instance1, self.name)
229 d2 = getattr(instance2, self.name)
231 _joinNamePath(instance1._name, self.name),
232 _joinNamePath(instance2._name, self.name)
234 if not compareScalars(
"isnone for %s" % name, d1
is None, d2
is None, output=output):
236 if d1
is None and d2
is None:
238 if not compareScalars(
"keys for %s" % name, set(d1.keys()), set(d2.keys()), output=output):
241 for k, v1
in d1.items():
244 rtol=rtol, atol=atol, output=output)
245 if not result
and shortcut:
247 equal = equal
and result
def __init__(self, config, field, value, at, label, setHistory=True)
def __setitem__(self, k, x, at=None, label="setitem", setHistory=True)
def __get__(self, instance, owner=None, at=None, label="default")
def _setup(self, doc, dtype, default, check, optional, source)
def getStackFrame(relative=0)
def __delitem__(self, k, at=None, label="delitem", setHistory=True)
def validate(self, instance)
def __contains__(self, k)
def __setattr__(self, attr, value, at=None, label="assignment")
def compareScalars(name, v1, v2, output, rtol=1E-8, atol=1E-8, dtype=None)
def __init__(self, doc, keytype, itemtype, default=None, optional=False, dictCheck=None, itemCheck=None)
def toDict(self, instance)
def getComparisonName(name1, name2)
def __set__(self, instance, value, at=None, label="assignment")