23 __all__ = [
"ConfigField"]
25 from .config
import Config, Field, FieldValidationError, _joinNamePath, _typeStr
26 from .comparison
import compareConfigs, getComparisonName
27 from .callStack
import getCallStack, getStackFrame
31 """A configuration field (`~lsst.pex.config.Field` subclass) that takes a 32 `~lsst.pex.config.Config`-type as a value. 37 A description of the configuration field. 38 dtype : `lsst.pex.config.Config`-type 39 The type of the field, which must be a subclass of 40 `lsst.pex.config.Config`. 41 default : `lsst.pex.config.Config`, optional 42 If default is `None`, the field will default to a default-constructed 43 instance of ``dtype``. Additionally, to allow for fewer deep-copies, 44 assigning an instance of ``ConfigField`` to ``dtype`` itself, is 45 considered equivalent to assigning a default-constructed sub-config. 46 This means that the argument default can be ``dtype``, as well as an 47 instance of ``dtype``. 48 check : callable, optional 49 A callback function that validates the field's value, returning `True` 50 if the value is valid, and `False` otherwise. 66 The behavior of this type of field is much like that of the base `Field` 69 Assigning to ``ConfigField`` will update all of the fields in the 73 def __init__(self, doc, dtype, default=None, check=None):
74 if not issubclass(dtype, Config):
75 raise ValueError(
"dtype=%s is not a subclass of Config" %
80 self.
_setup(doc=doc, dtype=dtype, default=default, check=check,
81 optional=
False, source=source)
84 if instance
is None or not isinstance(instance, Config):
87 value = instance._storage.get(self.name,
None)
94 def __set__(self, instance, value, at=None, label="assignment"):
97 "Cannot modify a frozen Config")
98 name = _joinNamePath(prefix=instance._name, name=self.name)
101 msg =
"Value %s is of incorrect type %s. Expected %s" % \
102 (value, _typeStr(value), _typeStr(self.
dtype))
108 oldValue = instance._storage.get(self.name,
None)
110 if value == self.
dtype:
111 instance._storage[self.name] = self.
dtype(__name=name, __at=at, __label=label)
113 instance._storage[self.name] = self.
dtype(__name=name, __at=at,
114 __label=label, **value._storage)
116 if value == self.
dtype:
118 oldValue.update(__at=at, __label=label, **value._storage)
119 history = instance._history.setdefault(self.name, [])
120 history.append((
"config value set", at, label))
123 """Rename the field in a `~lsst.pex.config.Config` (for internal use 128 instance : `lsst.pex.config.Config` 129 The config instance that contains this field. 133 This method is invoked by the `lsst.pex.config.Config` object that 134 contains this field and should not be called directly. 136 Renaming is only relevant for `~lsst.pex.config.Field` instances that 137 hold subconfigs. `~lsst.pex.config.Fields` that hold subconfigs should 138 rename each subconfig with the full field name as generated by 139 `lsst.pex.config.config._joinNamePath`. 142 value._rename(_joinNamePath(instance._name, self.name))
144 def _collectImports(self, instance, imports):
146 value._collectImports()
147 imports |= value._imports
149 def save(self, outfile, instance):
150 """Save this field to a file (for internal use only). 154 outfile : file-like object 155 A writeable field handle. 157 The `Config` instance that contains this field. 161 This method is invoked by the `~lsst.pex.config.Config` object that 162 contains this field and should not be called directly. 164 The output consists of the documentation string 165 (`lsst.pex.config.Field.doc`) formatted as a Python comment. The second 166 line is formatted as an assignment: ``{fullname}={value}``. 168 This output can be executed with Python. 174 """Make this field read-only. 178 instance : `lsst.pex.config.Config` 179 The config instance that contains this field. 183 Freezing is only relevant for fields that hold subconfigs. Fields which 184 hold subconfigs should freeze each subconfig. 186 **Subclasses should implement this method.** 192 """Convert the field value so that it can be set as the value of an 193 item in a `dict` (for internal use only). 198 The `Config` that contains this field. 203 The field's value. See *Notes*. 207 This method invoked by the owning `~lsst.pex.config.Config` object and 208 should not be called directly. 210 Simple values are passed through. Complex data structures must be 211 manipulated. For example, a `~lsst.pex.config.Field` holding a 212 subconfig should, instead of the subconfig object, return a `dict` 213 where the keys are the field names in the subconfig, and the values are 214 the field values in the subconfig. 217 return value.toDict()
220 """Validate the field (for internal use only). 224 instance : `lsst.pex.config.Config` 225 The config instance that contains this field. 229 lsst.pex.config.FieldValidationError 230 Raised if verification fails. 234 This method provides basic validation: 236 - Ensures that the value is not `None` if the field is not optional. 237 - Ensures type correctness. 238 - Ensures that the user-provided ``check`` function is valid. 240 Most `~lsst.pex.config.Field` subclasses should call 241 `lsst.pex.config.field.Field.validate` if they re-implement 242 `~lsst.pex.config.field.Field.validate`. 247 if self.
check is not None and not self.
check(value):
248 msg =
"%s is not a valid value" %
str(value)
251 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
252 """Compare two fields for equality. 254 Used by `ConfigField.compare`. 258 instance1 : `lsst.pex.config.Config` 259 Left-hand side config instance to compare. 260 instance2 : `lsst.pex.config.Config` 261 Right-hand side config instance to compare. 263 If `True`, this function returns as soon as an inequality if found. 265 Relative tolerance for floating point comparisons. 267 Absolute tolerance for floating point comparisons. 269 A callable that takes a string, used (possibly repeatedly) to report inequalities. 274 `True` if the fields are equal, `False` otherwise. 278 Floating point comparisons are performed by `numpy.allclose`. 280 c1 = getattr(instance1, self.name)
281 c2 = getattr(instance2, self.name)
283 _joinNamePath(instance1._name, self.name),
284 _joinNamePath(instance2._name, self.name)
286 return compareConfigs(name, c1, c2, shortcut=shortcut, rtol=rtol, atol=atol, output=output)
def __get__(self, instance, owner=None)
def __set__(self, instance, value, at=None, label="assignment")
def compareConfigs(name, c1, c2, shortcut=True, rtol=1E-8, atol=1E-8, output=None)
def __init__(self, doc, dtype, default=None, check=None)
def __get__(self, instance, owner=None, at=None, label="default")
def _setup(self, doc, dtype, default, check, optional, source)
def freeze(self, instance)
def getStackFrame(relative=0)
def toDict(self, instance)
def rename(self, instance)
def save(self, outfile, instance)
def __set__(self, instance, value, at=None, label='assignment')
def validate(self, instance)
def getComparisonName(name1, name2)