28 __all__ = [
"ListField"]
 
   30 import collections.abc
 
   32 from .config 
import Field, FieldValidationError, _typeStr, _autocast, _joinNamePath
 
   33 from .comparison 
import compareScalars, getComparisonName
 
   34 from .callStack 
import getCallStack, getStackFrame
 
   37 class List(collections.abc.MutableSequence):
 
   38     """List collection used internally by `ListField`. 
   42     config : `lsst.pex.config.Config` 
   43         Config instance that contains the ``field``. 
   45         Instance of the `ListField` using this ``List``. 
   47         Sequence of values that are inserted into this ``List``. 
   48     at : `list` of `lsst.pex.config.callStack.StackFrame` 
   49         The call stack (created by `lsst.pex.config.callStack.getCallStack`). 
   51         Event label for the history. 
   52     setHistory : `bool`, optional 
   53         Enable setting the field's history, using the value of the ``at`` 
   54         parameter. Default is `True`. 
   59         Raised if an item in the ``value`` parameter does not have the 
   60         appropriate type for this field or does not pass the 
   61         `ListField.itemCheck` method of the ``field`` parameter. 
   64     def __init__(self, config, field, value, at, label, setHistory=True):
 
   72                 for i, x 
in enumerate(value):
 
   73                     self.
insert(i, x, setHistory=
False)
 
   75                 msg = 
"Value %s is of incorrect type %s. Sequence type expected" % (value, _typeStr(value))
 
   81         """Validate an item to determine if it can be included in the list. 
   86             Index of the item in the `list`. 
   93             Raised if an item in the ``value`` parameter does not have the 
   94             appropriate type for this field or does not pass the field's 
   95             `ListField.itemCheck` method. 
   98         if not isinstance(x, self.
_field.itemtype) 
and x 
is not None:
 
   99             msg = 
"Item at position %d with value %s is of incorrect type %s. Expected %s" % \
 
  100                 (i, x, _typeStr(x), _typeStr(self.
_field.itemtype))
 
  103         if self.
_field.itemCheck 
is not None and not self.
_field.itemCheck(x):
 
  104             msg = 
"Item at position %d is not a valid value: %s" % (i, x)
 
  108         """Sequence of items contained by the `List` (`list`). 
  112     history = property(
lambda x: x._history)
 
  113     """Read-only history. 
  117         return x 
in self.
_list 
  120         return len(self.
_list)
 
  122     def __setitem__(self, i, x, at=None, label="setitem", setHistory=True):
 
  125                                        "Cannot modify a frozen Config")
 
  126         if isinstance(i, slice):
 
  127             k, stop, step = i.indices(len(self))
 
  128             for j, xj 
in enumerate(x):
 
  129                 xj = _autocast(xj, self.
_field.itemtype)
 
  134             x = _autocast(x, self.
_field.itemtype)
 
  146     def __delitem__(self, i, at=None, label="delitem", setHistory=True):
 
  149                                        "Cannot modify a frozen Config")
 
  159     def insert(self, i, x, at=None, label="insert", setHistory=True):
 
  160         """Insert an item into the list at the given index. 
  165             Index where the item is inserted. 
  167             Item that is inserted. 
  168         at : `list` of `lsst.pex.config.callStack.StackFrame`, optional 
  169             The call stack (created by 
  170             `lsst.pex.config.callStack.getCallStack`). 
  171         label : `str`, optional 
  172             Event label for the history. 
  173         setHistory : `bool`, optional 
  174             Enable setting the field's history, using the value of the ``at`` 
  175             parameter. Default is `True`. 
  179         self.
__setitem__(slice(i, i), [x], at=at, label=label, setHistory=setHistory)
 
  182         return repr(self.
_list)
 
  185         return str(self.
_list)
 
  189             if len(self) != len(other):
 
  192             for i, j 
in zip(self, other):
 
  196         except AttributeError:
 
  201         return not self.
__eq__(other)
 
  204         if hasattr(getattr(self.__class__, attr, 
None), 
'__set__'):
 
  206             object.__setattr__(self, attr, value)
 
  207         elif attr 
in self.__dict__ 
or attr 
in [
"_field", 
"_config", 
"_history", 
"_list", 
"__doc__"]:
 
  209             object.__setattr__(self, attr, value)
 
  212             msg = 
"%s has no attribute %s" % (_typeStr(self.
_field), attr)
 
  217     """A configuration field (`~lsst.pex.config.Field` subclass) that contains 
  218     a list of values of a specific type. 
  223         A description of the field. 
  225         The data type of items in the list. 
  226     default : sequence, optional 
  227         The default items for the field. 
  228     optional : `bool`, optional 
  229         Set whether the field is *optional*. When `False`, 
  230         `lsst.pex.config.Config.validate` will fail if the field's value is 
  232     listCheck : callable, optional 
  233         A callable that validates the list as a whole. 
  234     itemCheck : callable, optional 
  235         A callable that validates individual items in the list. 
  236     length : `int`, optional 
  237         If set, this field must contain exactly ``length`` number of items. 
  238     minLength : `int`, optional 
  239         If set, this field must contain *at least* ``minLength`` number of 
  241     maxLength : `int`, optional 
  242         If set, this field must contain *no more than* ``maxLength`` number of 
  244     deprecated : None or `str`, optional 
  245         A description of why this Field is deprecated, including removal date. 
  246         If not None, the string is appended to the docstring for this Field. 
  260     def __init__(self, doc, dtype, default=None, optional=False,
 
  261                  listCheck=None, itemCheck=None,
 
  262                  length=None, minLength=None, maxLength=None,
 
  264         if dtype 
not in Field.supportedTypes:
 
  265             raise ValueError(
"Unsupported dtype %s" % _typeStr(dtype))
 
  266         if length 
is not None:
 
  268                 raise ValueError(
"'length' (%d) must be positive" % length)
 
  272             if maxLength 
is not None and maxLength <= 0:
 
  273                 raise ValueError(
"'maxLength' (%d) must be positive" % maxLength)
 
  274             if minLength 
is not None and maxLength 
is not None \
 
  275                     and minLength > maxLength:
 
  276                 raise ValueError(
"'maxLength' (%d) must be at least" 
  277                                  " as large as 'minLength' (%d)" % (maxLength, minLength))
 
  279         if listCheck 
is not None and not hasattr(listCheck, 
"__call__"):
 
  280             raise ValueError(
"'listCheck' must be callable")
 
  281         if itemCheck 
is not None and not hasattr(itemCheck, 
"__call__"):
 
  282             raise ValueError(
"'itemCheck' must be callable")
 
  285         self.
_setup(doc=doc, dtype=List, default=default, check=
None, optional=optional, source=source,
 
  286                     deprecated=deprecated)
 
  289         """Callable used to check the list as a whole. 
  293         """Callable used to validate individual items as they are inserted 
  298         """Data type of list items. 
  302         """Number of items that must be present in the list (or `None` to 
  303         disable checking the list's length). 
  307         """Minimum number of items that must be present in the list (or `None` 
  308         to disable checking the list's minimum length). 
  312         """Maximum number of items that must be present in the list (or `None` 
  313         to disable checking the list's maximum length). 
  317         """Validate the field. 
  321         instance : `lsst.pex.config.Config` 
  322             The config instance that contains this field. 
  326         lsst.pex.config.FieldValidationError 
  329             - The field is not optional, but the value is `None`. 
  330             - The list itself does not meet the requirements of the `length`, 
  331               `minLength`, or `maxLength` attributes. 
  332             - The `listCheck` callable returns `False`. 
  336         Individual item checks (`itemCheck`) are applied when each item is 
  337         set and are not re-checked by this method. 
  339         Field.validate(self, instance)
 
  341         if value 
is not None:
 
  342             lenValue = len(value)
 
  343             if self.
length is not None and not lenValue == self.
length:
 
  344                 msg = 
"Required list length=%d, got length=%d" % (self.
length, lenValue)
 
  347                 msg = 
"Minimum allowed list length=%d, got length=%d" % (self.
minLength, lenValue)
 
  350                 msg = 
"Maximum allowed list length=%d, got length=%d" % (self.
maxLength, lenValue)
 
  353                 msg = 
"%s is not a valid value" % str(value)
 
  356     def __set__(self, instance, value, at=None, label="assignment"):
 
  363         if value 
is not None:
 
  364             value = 
List(instance, self, value, at, label)
 
  366             history = instance._history.setdefault(self.name, [])
 
  367             history.append((value, at, label))
 
  369         instance._storage[self.name] = value
 
  372         """Convert the value of this field to a plain `list`. 
  374         `lsst.pex.config.Config.toDict` is the primary user of this method. 
  378         instance : `lsst.pex.config.Config` 
  379             The config instance that contains this field. 
  384             Plain `list` of items, or `None` if the field is not set. 
  387         return list(value) 
if value 
is not None else None 
  389     def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
 
  390         """Compare two config instances for equality with respect to this 
  393         `lsst.pex.config.config.compare` is the primary user of this method. 
  397         instance1 : `lsst.pex.config.Config` 
  398             Left-hand-side `~lsst.pex.config.Config` instance in the 
  400         instance2 : `lsst.pex.config.Config` 
  401             Right-hand-side `~lsst.pex.config.Config` instance in the 
  404             If `True`, return as soon as an **inequality** is found. 
  406             Relative tolerance for floating point comparisons. 
  408             Absolute tolerance for floating point comparisons. 
  410             If not None, a callable that takes a `str`, used (possibly 
  411             repeatedly) to report inequalities. 
  416             `True` if the fields are equal; `False` otherwise. 
  420         Floating point comparisons are performed by `numpy.allclose`. 
  422         l1 = getattr(instance1, self.name)
 
  423         l2 = getattr(instance2, self.name)
 
  425             _joinNamePath(instance1._name, self.name),
 
  426             _joinNamePath(instance2._name, self.name)
 
  428         if not compareScalars(
"isnone for %s" % name, l1 
is None, l2 
is None, output=output):
 
  430         if l1 
is None and l2 
is None:
 
  432         if not compareScalars(
"size for %s" % name, len(l1), len(l2), output=output):
 
  435         for n, v1, v2 
in zip(range(len(l1)), l1, l2):
 
  437                                     rtol=rtol, atol=atol, output=output)
 
  438             if not result 
and shortcut:
 
  440             equal = equal 
and result