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. 51 deprecated : None or `str`, optional 52 A description of why this Field is deprecated, including removal date. 53 If not None, the string is appended to the docstring for this Field. 69 The behavior of this type of field is much like that of the base `Field` 72 Assigning to ``ConfigField`` will update all of the fields in the 76 def __init__(self, doc, dtype, default=None, check=None, deprecated=None):
77 if not issubclass(dtype, Config):
78 raise ValueError(
"dtype=%s is not a subclass of Config" %
83 self.
_setup(doc=doc, dtype=dtype, default=default, check=check,
84 optional=
False, source=source, deprecated=deprecated)
87 if instance
is None or not isinstance(instance, Config):
90 value = instance._storage.get(self.name,
None)
97 def __set__(self, instance, value, at=None, label="assignment"):
100 "Cannot modify a frozen Config")
101 name = _joinNamePath(prefix=instance._name, name=self.name)
103 if value != self.
dtype and type(value) != self.
dtype:
104 msg =
"Value %s is of incorrect type %s. Expected %s" % \
105 (value, _typeStr(value), _typeStr(self.
dtype))
111 oldValue = instance._storage.get(self.name,
None)
113 if value == self.
dtype:
114 instance._storage[self.name] = self.
dtype(__name=name, __at=at, __label=label)
116 instance._storage[self.name] = self.
dtype(__name=name, __at=at,
117 __label=label, **value._storage)
119 if value == self.
dtype:
121 oldValue.update(__at=at, __label=label, **value._storage)
122 history = instance._history.setdefault(self.name, [])
123 history.append((
"config value set", at, label))
126 """Rename the field in a `~lsst.pex.config.Config` (for internal use 131 instance : `lsst.pex.config.Config` 132 The config instance that contains this field. 136 This method is invoked by the `lsst.pex.config.Config` object that 137 contains this field and should not be called directly. 139 Renaming is only relevant for `~lsst.pex.config.Field` instances that 140 hold subconfigs. `~lsst.pex.config.Fields` that hold subconfigs should 141 rename each subconfig with the full field name as generated by 142 `lsst.pex.config.config._joinNamePath`. 145 value._rename(_joinNamePath(instance._name, self.name))
147 def _collectImports(self, instance, imports):
149 value._collectImports()
150 imports |= value._imports
152 def save(self, outfile, instance):
153 """Save this field to a file (for internal use only). 157 outfile : file-like object 158 A writeable field handle. 160 The `Config` instance that contains this field. 164 This method is invoked by the `~lsst.pex.config.Config` object that 165 contains this field and should not be called directly. 167 The output consists of the documentation string 168 (`lsst.pex.config.Field.doc`) formatted as a Python comment. The second 169 line is formatted as an assignment: ``{fullname}={value}``. 171 This output can be executed with Python. 177 """Make this field read-only. 181 instance : `lsst.pex.config.Config` 182 The config instance that contains this field. 186 Freezing is only relevant for fields that hold subconfigs. Fields which 187 hold subconfigs should freeze each subconfig. 189 **Subclasses should implement this method.** 195 """Convert the field value so that it can be set as the value of an 196 item in a `dict` (for internal use only). 201 The `Config` that contains this field. 206 The field's value. See *Notes*. 210 This method invoked by the owning `~lsst.pex.config.Config` object and 211 should not be called directly. 213 Simple values are passed through. Complex data structures must be 214 manipulated. For example, a `~lsst.pex.config.Field` holding a 215 subconfig should, instead of the subconfig object, return a `dict` 216 where the keys are the field names in the subconfig, and the values are 217 the field values in the subconfig. 220 return value.toDict()
223 """Validate the field (for internal use only). 227 instance : `lsst.pex.config.Config` 228 The config instance that contains this field. 232 lsst.pex.config.FieldValidationError 233 Raised if verification fails. 237 This method provides basic validation: 239 - Ensures that the value is not `None` if the field is not optional. 240 - Ensures type correctness. 241 - Ensures that the user-provided ``check`` function is valid. 243 Most `~lsst.pex.config.Field` subclasses should call 244 `lsst.pex.config.field.Field.validate` if they re-implement 245 `~lsst.pex.config.field.Field.validate`. 250 if self.
check is not None and not self.
check(value):
251 msg =
"%s is not a valid value" % str(value)
254 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
255 """Compare two fields for equality. 257 Used by `ConfigField.compare`. 261 instance1 : `lsst.pex.config.Config` 262 Left-hand side config instance to compare. 263 instance2 : `lsst.pex.config.Config` 264 Right-hand side config instance to compare. 266 If `True`, this function returns as soon as an inequality if found. 268 Relative tolerance for floating point comparisons. 270 Absolute tolerance for floating point comparisons. 272 A callable that takes a string, used (possibly repeatedly) to report inequalities. 277 `True` if the fields are equal, `False` otherwise. 281 Floating point comparisons are performed by `numpy.allclose`. 283 c1 = getattr(instance1, self.name)
284 c2 = getattr(instance2, self.name)
286 _joinNamePath(instance1._name, self.name),
287 _joinNamePath(instance2._name, self.name)
289 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 __init__(self, doc, dtype, default=None, check=None, deprecated=None)
def compareConfigs(name, c1, c2, shortcut=True, rtol=1E-8, atol=1E-8, output=None)
def __get__(self, instance, owner=None, at=None, label="default")
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 _setup(self, doc, dtype, default, check, optional, source, deprecated)
def validate(self, instance)
def getComparisonName(name1, name2)