22 __all__ = [
"ConfigField"]
24 from .config
import Config, Field, FieldValidationError, _joinNamePath, _typeStr
25 from .comparison
import compareConfigs, getComparisonName
26 from .callStack
import getCallStack, getStackFrame
30 """A configuration field (`~lsst.pex.config.Field` subclass) that takes a 31 `~lsst.pex.config.Config`-type as a value. 36 A description of the configuration field. 37 dtype : `lsst.pex.config.Config`-type 38 The type of the field, which must be a subclass of 39 `lsst.pex.config.Config`. 40 default : `lsst.pex.config.Config`, optional 41 If default is `None`, the field will default to a default-constructed 42 instance of ``dtype``. Additionally, to allow for fewer deep-copies, 43 assigning an instance of ``ConfigField`` to ``dtype`` itself, is 44 considered equivalent to assigning a default-constructed sub-config. 45 This means that the argument default can be ``dtype``, as well as an 46 instance of ``dtype``. 47 check : callable, optional 48 A callback function that validates the field's value, returning `True` 49 if the value is valid, and `False` otherwise. 50 deprecated : None or `str`, optional 51 A description of why this Field is deprecated, including removal date. 52 If not None, the string is appended to the docstring for this Field. 68 The behavior of this type of field is much like that of the base `Field` 71 Assigning to ``ConfigField`` will update all of the fields in the 75 def __init__(self, doc, dtype, default=None, check=None, deprecated=None):
76 if not issubclass(dtype, Config):
77 raise ValueError(
"dtype=%s is not a subclass of Config" %
82 self.
_setup(doc=doc, dtype=dtype, default=default, check=check,
83 optional=
False, source=source, deprecated=deprecated)
86 if instance
is None or not isinstance(instance, Config):
89 value = instance._storage.get(self.name,
None)
96 def __set__(self, instance, value, at=None, label="assignment"):
99 "Cannot modify a frozen Config")
100 name = _joinNamePath(prefix=instance._name, name=self.name)
102 if value != self.
dtype and type(value) != self.
dtype:
103 msg =
"Value %s is of incorrect type %s. Expected %s" % \
104 (value, _typeStr(value), _typeStr(self.
dtype))
110 oldValue = instance._storage.get(self.name,
None)
112 if value == self.
dtype:
113 instance._storage[self.name] = self.
dtype(__name=name, __at=at, __label=label)
115 instance._storage[self.name] = self.
dtype(__name=name, __at=at,
116 __label=label, **value._storage)
118 if value == self.
dtype:
120 oldValue.update(__at=at, __label=label, **value._storage)
121 history = instance._history.setdefault(self.name, [])
122 history.append((
"config value set", at, label))
125 """Rename the field in a `~lsst.pex.config.Config` (for internal use 130 instance : `lsst.pex.config.Config` 131 The config instance that contains this field. 135 This method is invoked by the `lsst.pex.config.Config` object that 136 contains this field and should not be called directly. 138 Renaming is only relevant for `~lsst.pex.config.Field` instances that 139 hold subconfigs. `~lsst.pex.config.Fields` that hold subconfigs should 140 rename each subconfig with the full field name as generated by 141 `lsst.pex.config.config._joinNamePath`. 144 value._rename(_joinNamePath(instance._name, self.name))
146 def _collectImports(self, instance, imports):
148 value._collectImports()
149 imports |= value._imports
151 def save(self, outfile, instance):
152 """Save this field to a file (for internal use only). 156 outfile : file-like object 157 A writeable field handle. 159 The `Config` instance that contains this field. 163 This method is invoked by the `~lsst.pex.config.Config` object that 164 contains this field and should not be called directly. 166 The output consists of the documentation string 167 (`lsst.pex.config.Field.doc`) formatted as a Python comment. The second 168 line is formatted as an assignment: ``{fullname}={value}``. 170 This output can be executed with Python. 176 """Make this field read-only. 180 instance : `lsst.pex.config.Config` 181 The config instance that contains this field. 185 Freezing is only relevant for fields that hold subconfigs. Fields which 186 hold subconfigs should freeze each subconfig. 188 **Subclasses should implement this method.** 194 """Convert the field value so that it can be set as the value of an 195 item in a `dict` (for internal use only). 200 The `Config` that contains this field. 205 The field's value. See *Notes*. 209 This method invoked by the owning `~lsst.pex.config.Config` object and 210 should not be called directly. 212 Simple values are passed through. Complex data structures must be 213 manipulated. For example, a `~lsst.pex.config.Field` holding a 214 subconfig should, instead of the subconfig object, return a `dict` 215 where the keys are the field names in the subconfig, and the values are 216 the field values in the subconfig. 219 return value.toDict()
222 """Validate the field (for internal use only). 226 instance : `lsst.pex.config.Config` 227 The config instance that contains this field. 231 lsst.pex.config.FieldValidationError 232 Raised if verification fails. 236 This method provides basic validation: 238 - Ensures that the value is not `None` if the field is not optional. 239 - Ensures type correctness. 240 - Ensures that the user-provided ``check`` function is valid. 242 Most `~lsst.pex.config.Field` subclasses should call 243 `lsst.pex.config.field.Field.validate` if they re-implement 244 `~lsst.pex.config.field.Field.validate`. 249 if self.
check is not None and not self.
check(value):
250 msg =
"%s is not a valid value" % str(value)
253 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
254 """Compare two fields for equality. 256 Used by `ConfigField.compare`. 260 instance1 : `lsst.pex.config.Config` 261 Left-hand side config instance to compare. 262 instance2 : `lsst.pex.config.Config` 263 Right-hand side config instance to compare. 265 If `True`, this function returns as soon as an inequality if found. 267 Relative tolerance for floating point comparisons. 269 Absolute tolerance for floating point comparisons. 271 A callable that takes a string, used (possibly repeatedly) to report inequalities. 276 `True` if the fields are equal, `False` otherwise. 280 Floating point comparisons are performed by `numpy.allclose`. 282 c1 = getattr(instance1, self.name)
283 c2 = getattr(instance2, self.name)
285 _joinNamePath(instance1._name, self.name),
286 _joinNamePath(instance2._name, self.name)
288 return compareConfigs(name, c1, c2, shortcut=shortcut, rtol=rtol, atol=atol, output=output)
def compareConfigs(name, c1, c2, shortcut=True, rtol=1E-8, atol=1E-8, output=None)
def __get__(self, instance, owner=None)
def validate(self, instance)
def rename(self, instance)
def getStackFrame(relative=0)
def __get__(self, instance, owner=None, at=None, label="default")
def __set__(self, instance, value, at=None, label='assignment')
def save(self, outfile, instance)
def __init__(self, doc, dtype, default=None, check=None, deprecated=None)
def getComparisonName(name1, name2)
def __set__(self, instance, value, at=None, label="assignment")
def toDict(self, instance)
def freeze(self, instance)
def _setup(self, doc, dtype, default, check, optional, source, deprecated)