28 __all__ = [
"ConfigField"]
30 from .config
import Config, Field, FieldValidationError, _joinNamePath, _typeStr
31 from .comparison
import compareConfigs, getComparisonName
32 from .callStack
import getCallStack, getStackFrame
36 """A configuration field (`~lsst.pex.config.Field` subclass) that takes a
37 `~lsst.pex.config.Config`-type as a value.
42 A description of the configuration field.
43 dtype : `lsst.pex.config.Config`-type
44 The type of the field, which must be a subclass of
45 `lsst.pex.config.Config`.
46 default : `lsst.pex.config.Config`, optional
47 If default is `None`, the field will default to a default-constructed
48 instance of ``dtype``. Additionally, to allow for fewer deep-copies,
49 assigning an instance of ``ConfigField`` to ``dtype`` itself, is
50 considered equivalent to assigning a default-constructed sub-config.
51 This means that the argument default can be ``dtype``, as well as an
52 instance of ``dtype``.
53 check : callable, optional
54 A callback function that validates the field's value, returning `True`
55 if the value is valid, and `False` otherwise.
56 deprecated : None or `str`, optional
57 A description of why this Field is deprecated, including removal date.
58 If not None, the string is appended to the docstring for this Field.
74 The behavior of this type of field is much like that of the base `Field`
77 Assigning to ``ConfigField`` will update all of the fields in the
81 def __init__(self, doc, dtype, default=None, check=None, deprecated=None):
82 if not issubclass(dtype, Config):
83 raise ValueError(
"dtype=%s is not a subclass of Config" %
88 self.
_setup_setup(doc=doc, dtype=dtype, default=default, check=check,
89 optional=
False, source=source, deprecated=deprecated)
92 if instance
is None or not isinstance(instance, Config):
95 value = instance._storage.get(self.name,
None)
98 at.insert(0, self.
sourcesource)
102 def __set__(self, instance, value, at=None, label="assignment"):
105 "Cannot modify a frozen Config")
106 name = _joinNamePath(prefix=instance._name, name=self.name)
109 msg =
"Value %s is of incorrect type %s. Expected %s" % \
110 (value, _typeStr(value), _typeStr(self.
dtypedtype))
116 oldValue = instance._storage.get(self.name,
None)
118 if value == self.
dtypedtype:
119 instance._storage[self.name] = self.
dtypedtype(__name=name, __at=at, __label=label)
121 instance._storage[self.name] = self.
dtypedtype(__name=name, __at=at,
122 __label=label, **value._storage)
124 if value == self.
dtypedtype:
126 oldValue.update(__at=at, __label=label, **value._storage)
127 history = instance._history.setdefault(self.name, [])
128 history.append((
"config value set", at, label))
131 """Rename the field in a `~lsst.pex.config.Config` (for internal use
136 instance : `lsst.pex.config.Config`
137 The config instance that contains this field.
141 This method is invoked by the `lsst.pex.config.Config` object that
142 contains this field and should not be called directly.
144 Renaming is only relevant for `~lsst.pex.config.Field` instances that
145 hold subconfigs. `~lsst.pex.config.Fields` that hold subconfigs should
146 rename each subconfig with the full field name as generated by
147 `lsst.pex.config.config._joinNamePath`.
150 value._rename(_joinNamePath(instance._name, self.name))
152 def _collectImports(self, instance, imports):
154 value._collectImports()
155 imports |= value._imports
157 def save(self, outfile, instance):
158 """Save this field to a file (for internal use only).
162 outfile : file-like object
163 A writeable field handle.
165 The `Config` instance that contains this field.
169 This method is invoked by the `~lsst.pex.config.Config` object that
170 contains this field and should not be called directly.
172 The output consists of the documentation string
173 (`lsst.pex.config.Field.doc`) formatted as a Python comment. The second
174 line is formatted as an assignment: ``{fullname}={value}``.
176 This output can be executed with Python.
182 """Make this field read-only.
186 instance : `lsst.pex.config.Config`
187 The config instance that contains this field.
191 Freezing is only relevant for fields that hold subconfigs. Fields which
192 hold subconfigs should freeze each subconfig.
194 **Subclasses should implement this method.**
200 """Convert the field value so that it can be set as the value of an
201 item in a `dict` (for internal use only).
206 The `Config` that contains this field.
211 The field's value. See *Notes*.
215 This method invoked by the owning `~lsst.pex.config.Config` object and
216 should not be called directly.
218 Simple values are passed through. Complex data structures must be
219 manipulated. For example, a `~lsst.pex.config.Field` holding a
220 subconfig should, instead of the subconfig object, return a `dict`
221 where the keys are the field names in the subconfig, and the values are
222 the field values in the subconfig.
225 return value.toDict()
228 """Validate the field (for internal use only).
232 instance : `lsst.pex.config.Config`
233 The config instance that contains this field.
237 lsst.pex.config.FieldValidationError
238 Raised if verification fails.
242 This method provides basic validation:
244 - Ensures that the value is not `None` if the field is not optional.
245 - Ensures type correctness.
246 - Ensures that the user-provided ``check`` function is valid.
248 Most `~lsst.pex.config.Field` subclasses should call
249 `lsst.pex.config.field.Field.validate` if they re-implement
250 `~lsst.pex.config.field.Field.validate`.
255 if self.
checkcheck
is not None and not self.
checkcheck(value):
256 msg =
"%s is not a valid value" % str(value)
259 def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
260 """Compare two fields for equality.
262 Used by `ConfigField.compare`.
266 instance1 : `lsst.pex.config.Config`
267 Left-hand side config instance to compare.
268 instance2 : `lsst.pex.config.Config`
269 Right-hand side config instance to compare.
271 If `True`, this function returns as soon as an inequality if found.
273 Relative tolerance for floating point comparisons.
275 Absolute tolerance for floating point comparisons.
277 A callable that takes a string, used (possibly repeatedly) to
283 `True` if the fields are equal, `False` otherwise.
287 Floating point comparisons are performed by `numpy.allclose`.
289 c1 = getattr(instance1, self.name)
290 c2 = getattr(instance2, self.name)
292 _joinNamePath(instance1._name, self.name),
293 _joinNamePath(instance2._name, self.name)
295 return compareConfigs(name, c1, c2, shortcut=shortcut, rtol=rtol, atol=atol, output=output)
def __get__(self, instance, owner=None, at=None, label="default")
def __set__(self, instance, value, at=None, label='assignment')
def _setup(self, doc, dtype, default, check, optional, source, deprecated)
def validate(self, instance)
def rename(self, instance)
def __get__(self, instance, owner=None)
def freeze(self, instance)
def toDict(self, instance)
def save(self, outfile, instance)
def __set__(self, instance, value, at=None, label="assignment")
def __init__(self, doc, dtype, default=None, check=None, deprecated=None)
def getStackFrame(relative=0)
def compareConfigs(name, c1, c2, shortcut=True, rtol=1E-8, atol=1E-8, output=None)
def getComparisonName(name1, name2)