24 from .config
import Config, Field, FieldValidationError, _joinNamePath, _typeStr
25 from .comparison
import compareConfigs, getComparisonName
27 __all__ = [
"ConfigField"]
31 Defines a field which is itself a Config.
33 The behavior of this type of field is much like that of the base Field type.
35 Note that dtype must be a subclass of Config.
37 If default=None, the field will default to a default-constructed
40 Additionally, to allow for fewer deep-copies, assigning an instance of
41 ConfigField to dtype itself, is considered equivalent to assigning a
42 default-constructed sub-config. This means that the argument default can be
43 dtype, as well as an instance of dtype.
45 Assigning to ConfigField will update all of the fields in the config.
48 def __init__(self, doc, dtype, default=None, check=None):
49 if not issubclass(dtype, Config):
50 raise ValueError(
"dtype=%s is not a subclass of Config" % \
54 source = traceback.extract_stack(limit=2)[0]
55 self._setup( doc=doc, dtype=dtype, default=default, check=check,
56 optional=
False, source=source)
59 if instance
is None or not isinstance(instance, Config):
62 value = instance._storage.get(self.name,
None)
64 at = traceback.extract_stack()[:-1]+[self.source]
65 self.
__set__(instance, self.default, at=at, label=
"default")
68 def __set__(self, instance, value, at=None, label="assignment"):
70 raise FieldValidationError(self, instance, \
71 "Cannot modify a frozen Config")
74 if value != self.dtype
and type(value) != self.dtype:
75 msg =
"Value %s is of incorrect type %s. Expected %s" %\
77 raise FieldValidationError(self, instance, msg)
80 at = traceback.extract_stack()[:-1]
82 oldValue = instance._storage.get(self.name,
None)
84 if value == self.dtype:
85 instance._storage[self.name] = self.dtype(
86 __name=name, __at=at, __label=label)
88 instance._storage[self.name] = self.dtype(
89 __name=name, __at=at, __label=label, **value._storage)
91 if value == self.dtype:
93 oldValue.update(__at=at, __label=label, **value._storage)
94 history = instance._history.setdefault(self.name, [])
95 history.append((
"config value set", at, label))
101 def save(self, outfile, instance):
111 return value.toDict()
117 if self.check
is not None and not self.check(value):
118 msg =
"%s is not a valid value"%str(value)
119 raise FieldValidationError(self, instance, msg)
121 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
122 """Helper function for Config.compare; used to compare two fields for equality.
124 @param[in] instance1 LHS Config instance to compare.
125 @param[in] instance2 RHS Config instance to compare.
126 @param[in] shortcut If True, return as soon as an inequality is found.
127 @param[in] rtol Relative tolerance for floating point comparisons.
128 @param[in] atol Absolute tolerance for floating point comparisons.
129 @param[in] output If not None, a callable that takes a string, used (possibly repeatedly)
130 to report inequalities.
132 Floating point comparisons are performed by numpy.allclose; refer to that for details.
134 c1 = getattr(instance1, self.name)
135 c2 = getattr(instance2, self.name)
140 return compareConfigs(name, c1, c2, shortcut=shortcut, rtol=rtol, atol=atol, output=output)