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(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)
 
  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.
dtype))
 
  116         oldValue = instance._storage.get(self.name, 
None)
 
  118             if value == self.
dtype:
 
  119                 instance._storage[self.name] = self.
dtype(__name=name, __at=at, __label=label)
 
  121                 instance._storage[self.name] = self.
dtype(__name=name, __at=at,
 
  122                                                           __label=label, **value._storage)
 
  124             if value == self.
dtype:
 
  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.
check is not None and not self.
check(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)