25 from .config
import Field, FieldValidationError, _typeStr, _autocast, _joinNamePath
26 from .comparison
import compareScalars, getComparisonName
28 __all__ = [
"ListField"]
30 class List(collections.MutableSequence):
31 def __init__(self, config, field, value, at, label, setHistory=True):
34 self.
_history = self._config._history.setdefault(self._field.name, [])
39 for i, x
in enumerate(value):
40 self.
insert(i, x, setHistory=
False)
42 msg =
"Value %s is of incorrect type %s. Sequence type expected"%(value,
_typeStr(value))
45 self.history.append((list(self.
_list), at, label))
49 if not isinstance(x, self._field.itemtype)
and x
is not None:
50 msg=
"Item at position %d with value %s is of incorrect type %s. Expected %s"%\
54 if self._field.itemCheck
is not None and not self._field.itemCheck(x):
55 msg=
"Item at position %d is not a valid value: %s"%(i, x)
62 history = property(
lambda x: x._history)
68 def __setitem__(self, i, x, at=None, label="setitem", setHistory=True):
69 if self._config._frozen:
71 "Cannot modify a frozen Config")
72 if isinstance(i, slice):
73 k, stop, step = i.indices(len(self))
74 for j, xj
in enumerate(x):
86 at = traceback.extract_stack()[:-1]
87 self.history.append((list(self.
_list), at, label))
92 def __delitem__(self, i, at =None, label="delitem", setHistory=True):
93 if self._config._frozen:
95 "Cannot modify a frozen Config")
99 at = traceback.extract_stack()[:-1]
100 self.history.append((list(self.
_list), at, label))
104 def insert(self, i, x, at=None, label="insert", setHistory=True):
106 at = traceback.extract_stack()[:-1]
107 self.
__setitem__(slice(i,i), [x], at=at, label=label, setHistory=setHistory)
115 if len(self) != len(other):
118 for i,j
in zip(self, other):
119 if i != j:
return False
121 except AttributeError:
126 return not self.
__eq__(other)
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",
"_list",
"__doc__"]:
134 object.__setattr__(self, attr, value)
143 Defines a field which is a container of values of type dtype
145 If length is not None, then instances of this field must match this length
147 If minLength is not None, then instances of the field must be no shorter
149 If maxLength is not None, then instances of the field must be no longer
152 Additionally users can provide two check functions:
153 listCheck - used to validate the list as a whole, and
154 itemCheck - used to validate each item individually
156 def __init__(self, doc, dtype, default=None, optional=False,
157 listCheck=
None, itemCheck=
None,
158 length=
None, minLength=
None, maxLength=
None):
159 if dtype
not in Field.supportedTypes:
160 raise ValueError(
"Unsupported dtype %s"%
_typeStr(dtype))
161 if length
is not None:
163 raise ValueError(
"'length' (%d) must be positive"%length)
167 if maxLength
is not None and maxLength <= 0:
168 raise ValueError(
"'maxLength' (%d) must be positive"%maxLength)
169 if minLength
is not None and maxLength
is not None \
170 and minLength > maxLength:
171 raise ValueError(
"'maxLength' (%d) must be at least as large as 'minLength' (%d)"%(maxLength, minLength))
173 if listCheck
is not None and not hasattr(listCheck,
"__call__"):
174 raise ValueError(
"'listCheck' must be callable")
175 if itemCheck
is not None and not hasattr(itemCheck,
"__call__"):
176 raise ValueError(
"'itemCheck' must be callable")
178 source = traceback.extract_stack(limit=2)[0]
179 self._setup( doc=doc, dtype=List, default=default, check=
None, optional=optional, source=source)
190 ListField validation ensures that non-optional fields are not None,
191 and that non-None values comply with length requirements and
192 that the list passes listCheck if supplied by the user.
193 Individual Item checks are applied at set time and are not re-checked.
195 Field.validate(self, instance)
196 value = self.__get__(instance)
197 if value
is not None:
199 if self.
length is not None and not lenValue == self.
length:
200 msg =
"Required list length=%d, got length=%d"%(self.
length, lenValue)
201 raise FieldValidationError(self, instance, msg)
203 msg =
"Minimum allowed list length=%d, got length=%d"%(self.
minLength, lenValue)
204 raise FieldValidationError(self, instance, msg)
206 msg =
"Maximum allowed list length=%d, got length=%d"%(self.
maxLength, lenValue)
207 raise FieldValidationError(self, instance, msg)
209 msg =
"%s is not a valid value"%str(value)
210 raise FieldValidationError(self, instance, msg)
212 def __set__(self, instance, value, at=None, label="assignment"):
214 raise FieldValidationError(self, instance,
"Cannot modify a frozen Config")
217 at = traceback.extract_stack()[:-1]
219 if value
is not None:
220 value =
List(instance, self, value, at, label)
222 history = instance._history.setdefault(self.name, [])
223 history.append((value, at, label))
225 instance._storage[self.name] = value
229 value = self.__get__(instance)
230 return list(value)
if value
is not None else None
232 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
233 """Helper function for Config.compare; used to compare two fields for equality.
235 @param[in] instance1 LHS Config instance to compare.
236 @param[in] instance2 RHS Config instance to compare.
237 @param[in] shortcut If True, return as soon as an inequality is found.
238 @param[in] rtol Relative tolerance for floating point comparisons.
239 @param[in] atol Absolute tolerance for floating point comparisons.
240 @param[in] output If not None, a callable that takes a string, used (possibly repeatedly)
241 to report inequalities.
243 Floating point comparisons are performed by numpy.allclose; refer to that for details.
245 l1 = getattr(instance1, self.name)
246 l2 = getattr(instance2, self.name)
251 if not compareScalars(
"isnone for %s" % name, l1
is None, l2
is None, output=output):
253 if l1
is None and l2
is None:
255 if not compareScalars(
"size for %s" % name, len(l1), len(l2), output=output):
258 for n, v1, v2
in zip(range(len(l1)), l1, l2):
259 result =
compareScalars(
"%s[%d]" % (name, n), v1, v2, dtype=self.dtype,
260 rtol=rtol, atol=atol, output=output)
261 if not result
and shortcut:
263 equal = equal
and result