LSSTApplications  16.0-10-g0ee56ad+5,16.0-11-ga33d1f2+5,16.0-12-g3ef5c14+3,16.0-12-g71e5ef5+18,16.0-12-gbdf3636+3,16.0-13-g118c103+3,16.0-13-g8f68b0a+3,16.0-15-gbf5c1cb+4,16.0-16-gfd17674+3,16.0-17-g7c01f5c+3,16.0-18-g0a50484+1,16.0-20-ga20f992+8,16.0-21-g0e05fd4+6,16.0-21-g15e2d33+4,16.0-22-g62d8060+4,16.0-22-g847a80f+4,16.0-25-gf00d9b8+1,16.0-28-g3990c221+4,16.0-3-gf928089+3,16.0-32-g88a4f23+5,16.0-34-gd7987ad+3,16.0-37-gc7333cb+2,16.0-4-g10fc685+2,16.0-4-g18f3627+26,16.0-4-g5f3a788+26,16.0-5-gaf5c3d7+4,16.0-5-gcc1f4bb+1,16.0-6-g3b92700+4,16.0-6-g4412fcd+3,16.0-6-g7235603+4,16.0-69-g2562ce1b+2,16.0-8-g14ebd58+4,16.0-8-g2df868b+1,16.0-8-g4cec79c+6,16.0-8-gadf6c7a+1,16.0-8-gfc7ad86,16.0-82-g59ec2a54a+1,16.0-9-g5400cdc+2,16.0-9-ge6233d7+5,master-g2880f2d8cf+3,v17.0.rc1
LSSTDataManagementBasePackage
listField.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 
23 __all__ = ["ListField"]
24 
25 import collections.abc
26 
27 from .config import Field, FieldValidationError, _typeStr, _autocast, _joinNamePath
28 from .comparison import compareScalars, getComparisonName
29 from .callStack import getCallStack, getStackFrame
30 
31 
32 class List(collections.abc.MutableSequence):
33  """List collection used internally by `ListField`.
34 
35  Parameters
36  ----------
37  config : `lsst.pex.config.Config`
38  Config instance that contains the ``field``.
39  field : `ListField`
40  Instance of the `ListField` using this ``List``.
41  value : sequence
42  Sequence of values that are inserted into this ``List``.
43  at : `list` of `lsst.pex.config.callStack.StackFrame`
44  The call stack (created by `lsst.pex.config.callStack.getCallStack`).
45  label : `str`
46  Event label for the history.
47  setHistory : `bool`, optional
48  Enable setting the field's history, using the value of the ``at``
49  parameter. Default is `True`.
50 
51  Raises
52  ------
53  FieldValidationError
54  Raised if an item in the ``value`` parameter does not have the
55  appropriate type for this field or does not pass the
56  `ListField.itemCheck` method of the ``field`` parameter.
57  """
58 
59  def __init__(self, config, field, value, at, label, setHistory=True):
60  self._field = field
61  self._config = config
62  self._history = self._config._history.setdefault(self._field.name, [])
63  self._list = []
64  self.__doc__ = field.doc
65  if value is not None:
66  try:
67  for i, x in enumerate(value):
68  self.insert(i, x, setHistory=False)
69  except TypeError:
70  msg = "Value %s is of incorrect type %s. Sequence type expected" % (value, _typeStr(value))
71  raise FieldValidationError(self._field, self._config, msg)
72  if setHistory:
73  self.history.append((list(self._list), at, label))
74 
75  def validateItem(self, i, x):
76  """Validate an item to determine if it can be included in the list.
77 
78  Parameters
79  ----------
80  i : `int`
81  Index of the item in the `list`.
82  x : object
83  Item in the `list`.
84 
85  Raises
86  ------
87  FieldValidationError
88  Raised if an item in the ``value`` parameter does not have the
89  appropriate type for this field or does not pass the field's
90  `ListField.itemCheck` method.
91  """
92 
93  if not isinstance(x, self._field.itemtype) and x is not None:
94  msg = "Item at position %d with value %s is of incorrect type %s. Expected %s" % \
95  (i, x, _typeStr(x), _typeStr(self._field.itemtype))
96  raise FieldValidationError(self._field, self._config, msg)
97 
98  if self._field.itemCheck is not None and not self._field.itemCheck(x):
99  msg = "Item at position %d is not a valid value: %s" % (i, x)
100  raise FieldValidationError(self._field, self._config, msg)
101 
102  def list(self):
103  """Sequence of items contained by the `List` (`list`).
104  """
105  return self._list
106 
107  history = property(lambda x: x._history)
108  """Read-only history.
109  """
110 
111  def __contains__(self, x):
112  return x in self._list
113 
114  def __len__(self):
115  return len(self._list)
116 
117  def __setitem__(self, i, x, at=None, label="setitem", setHistory=True):
118  if self._config._frozen:
119  raise FieldValidationError(self._field, self._config,
120  "Cannot modify a frozen Config")
121  if isinstance(i, slice):
122  k, stop, step = i.indices(len(self))
123  for j, xj in enumerate(x):
124  xj = _autocast(xj, self._field.itemtype)
125  self.validateItem(k, xj)
126  x[j] = xj
127  k += step
128  else:
129  x = _autocast(x, self._field.itemtype)
130  self.validateItem(i, x)
131 
132  self._list[i] = x
133  if setHistory:
134  if at is None:
135  at = getCallStack()
136  self.history.append((list(self._list), at, label))
137 
138  def __getitem__(self, i):
139  return self._list[i]
140 
141  def __delitem__(self, i, at=None, label="delitem", setHistory=True):
142  if self._config._frozen:
143  raise FieldValidationError(self._field, self._config,
144  "Cannot modify a frozen Config")
145  del self._list[i]
146  if setHistory:
147  if at is None:
148  at = getCallStack()
149  self.history.append((list(self._list), at, label))
150 
151  def __iter__(self):
152  return iter(self._list)
153 
154  def insert(self, i, x, at=None, label="insert", setHistory=True):
155  """Insert an item into the list at the given index.
156 
157  Parameters
158  ----------
159  i : `int`
160  Index where the item is inserted.
161  x : object
162  Item that is inserted.
163  at : `list` of `lsst.pex.config.callStack.StackFrame`, optional
164  The call stack (created by
165  `lsst.pex.config.callStack.getCallStack`).
166  label : `str`, optional
167  Event label for the history.
168  setHistory : `bool`, optional
169  Enable setting the field's history, using the value of the ``at``
170  parameter. Default is `True`.
171  """
172  if at is None:
173  at = getCallStack()
174  self.__setitem__(slice(i, i), [x], at=at, label=label, setHistory=setHistory)
175 
176  def __repr__(self):
177  return repr(self._list)
178 
179  def __str__(self):
180  return str(self._list)
181 
182  def __eq__(self, other):
183  try:
184  if len(self) != len(other):
185  return False
186 
187  for i, j in zip(self, other):
188  if i != j:
189  return False
190  return True
191  except AttributeError:
192  # other is not a sequence type
193  return False
194 
195  def __ne__(self, other):
196  return not self.__eq__(other)
197 
198  def __setattr__(self, attr, value, at=None, label="assignment"):
199  if hasattr(getattr(self.__class__, attr, None), '__set__'):
200  # This allows properties to work.
201  object.__setattr__(self, attr, value)
202  elif attr in self.__dict__ or attr in ["_field", "_config", "_history", "_list", "__doc__"]:
203  # This allows specific private attributes to work.
204  object.__setattr__(self, attr, value)
205  else:
206  # We throw everything else.
207  msg = "%s has no attribute %s" % (_typeStr(self._field), attr)
208  raise FieldValidationError(self._field, self._config, msg)
209 
210 
212  """A configuration field (`~lsst.pex.config.Field` subclass) that contains
213  a list of values of a specific type.
214 
215  Parameters
216  ----------
217  doc : `str`
218  A description of the field.
219  dtype : class
220  The data type of items in the list.
221  default : sequence, optional
222  The default items for the field.
223  optional : `bool`, optional
224  Set whether the field is *optional*. When `False`,
225  `lsst.pex.config.Config.validate` will fail if the field's value is
226  `None`.
227  listCheck : callable, optional
228  A callable that validates the list as a whole.
229  itemCheck : callable, optional
230  A callable that validates individual items in the list.
231  length : `int`, optional
232  If set, this field must contain exactly ``length`` number of items.
233  minLength : `int`, optional
234  If set, this field must contain *at least* ``minLength`` number of
235  items.
236  maxLength : `int`, optional
237  If set, this field must contain *no more than* ``maxLength`` number of
238  items.
239 
240  See also
241  --------
242  ChoiceField
243  ConfigChoiceField
244  ConfigDictField
245  ConfigField
246  ConfigurableField
247  DictField
248  Field
249  RangeField
250  RegistryField
251  """
252  def __init__(self, doc, dtype, default=None, optional=False,
253  listCheck=None, itemCheck=None,
254  length=None, minLength=None, maxLength=None):
255  if dtype not in Field.supportedTypes:
256  raise ValueError("Unsupported dtype %s" % _typeStr(dtype))
257  if length is not None:
258  if length <= 0:
259  raise ValueError("'length' (%d) must be positive" % length)
260  minLength = None
261  maxLength = None
262  else:
263  if maxLength is not None and maxLength <= 0:
264  raise ValueError("'maxLength' (%d) must be positive" % maxLength)
265  if minLength is not None and maxLength is not None \
266  and minLength > maxLength:
267  raise ValueError("'maxLength' (%d) must be at least"
268  " as large as 'minLength' (%d)" % (maxLength, minLength))
269 
270  if listCheck is not None and not hasattr(listCheck, "__call__"):
271  raise ValueError("'listCheck' must be callable")
272  if itemCheck is not None and not hasattr(itemCheck, "__call__"):
273  raise ValueError("'itemCheck' must be callable")
274 
275  source = getStackFrame()
276  self._setup(doc=doc, dtype=List, default=default, check=None, optional=optional, source=source)
277 
278  self.listCheck = listCheck
279  """Callable used to check the list as a whole.
280  """
281 
282  self.itemCheck = itemCheck
283  """Callable used to validate individual items as they are inserted
284  into the list.
285  """
286 
287  self.itemtype = dtype
288  """Data type of list items.
289  """
290 
291  self.length = length
292  """Number of items that must be present in the list (or `None` to
293  disable checking the list's length).
294  """
295 
296  self.minLength = minLength
297  """Minimum number of items that must be present in the list (or `None`
298  to disable checking the list's minimum length).
299  """
300 
301  self.maxLength = maxLength
302  """Maximum number of items that must be present in the list (or `None`
303  to disable checking the list's maximum length).
304  """
305 
306  def validate(self, instance):
307  """Validate the field.
308 
309  Parameters
310  ----------
311  instance : `lsst.pex.config.Config`
312  The config instance that contains this field.
313 
314  Raises
315  ------
316  lsst.pex.config.FieldValidationError
317  Raised if:
318 
319  - The field is not optional, but the value is `None`.
320  - The list itself does not meet the requirements of the `length`,
321  `minLength`, or `maxLength` attributes.
322  - The `listCheck` callable returns `False`.
323 
324  Notes
325  -----
326  Individual item checks (`itemCheck`) are applied when each item is
327  set and are not re-checked by this method.
328  """
329  Field.validate(self, instance)
330  value = self.__get__(instance)
331  if value is not None:
332  lenValue = len(value)
333  if self.length is not None and not lenValue == self.length:
334  msg = "Required list length=%d, got length=%d" % (self.length, lenValue)
335  raise FieldValidationError(self, instance, msg)
336  elif self.minLength is not None and lenValue < self.minLength:
337  msg = "Minimum allowed list length=%d, got length=%d" % (self.minLength, lenValue)
338  raise FieldValidationError(self, instance, msg)
339  elif self.maxLength is not None and lenValue > self.maxLength:
340  msg = "Maximum allowed list length=%d, got length=%d" % (self.maxLength, lenValue)
341  raise FieldValidationError(self, instance, msg)
342  elif self.listCheck is not None and not self.listCheck(value):
343  msg = "%s is not a valid value" % str(value)
344  raise FieldValidationError(self, instance, msg)
345 
346  def __set__(self, instance, value, at=None, label="assignment"):
347  if instance._frozen:
348  raise FieldValidationError(self, instance, "Cannot modify a frozen Config")
349 
350  if at is None:
351  at = getCallStack()
352 
353  if value is not None:
354  value = List(instance, self, value, at, label)
355  else:
356  history = instance._history.setdefault(self.name, [])
357  history.append((value, at, label))
358 
359  instance._storage[self.name] = value
360 
361  def toDict(self, instance):
362  """Convert the value of this field to a plain `list`.
363 
364  `lsst.pex.config.Config.toDict` is the primary user of this method.
365 
366  Parameters
367  ----------
368  instance : `lsst.pex.config.Config`
369  The config instance that contains this field.
370 
371  Returns
372  -------
373  `list`
374  Plain `list` of items, or `None` if the field is not set.
375  """
376  value = self.__get__(instance)
377  return list(value) if value is not None else None
378 
379  def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
380  """Compare two config instances for equality with respect to this
381  field.
382 
383  `lsst.pex.config.config.compare` is the primary user of this method.
384 
385  Parameters
386  ----------
387  instance1 : `lsst.pex.config.Config`
388  Left-hand-side `~lsst.pex.config.Config` instance in the
389  comparison.
390  instance2 : `lsst.pex.config.Config`
391  Right-hand-side `~lsst.pex.config.Config` instance in the
392  comparison.
393  shortcut : `bool`
394  If `True`, return as soon as an **inequality** is found.
395  rtol : `float`
396  Relative tolerance for floating point comparisons.
397  atol : `float`
398  Absolute tolerance for floating point comparisons.
399  output : callable
400  If not None, a callable that takes a `str`, used (possibly
401  repeatedly) to report inequalities.
402 
403  Returns
404  -------
405  equal : `bool`
406  `True` if the fields are equal; `False` otherwise.
407 
408  Notes
409  -----
410  Floating point comparisons are performed by `numpy.allclose`.
411  """
412  l1 = getattr(instance1, self.name)
413  l2 = getattr(instance2, self.name)
414  name = getComparisonName(
415  _joinNamePath(instance1._name, self.name),
416  _joinNamePath(instance2._name, self.name)
417  )
418  if not compareScalars("isnone for %s" % name, l1 is None, l2 is None, output=output):
419  return False
420  if l1 is None and l2 is None:
421  return True
422  if not compareScalars("size for %s" % name, len(l1), len(l2), output=output):
423  return False
424  equal = True
425  for n, v1, v2 in zip(range(len(l1)), l1, l2):
426  result = compareScalars("%s[%d]" % (name, n), v1, v2, dtype=self.dtype,
427  rtol=rtol, atol=atol, output=output)
428  if not result and shortcut:
429  return False
430  equal = equal and result
431  return equal
def __init__(self, doc, dtype, default=None, optional=False, listCheck=None, itemCheck=None, length=None, minLength=None, maxLength=None)
Definition: listField.py:254
def validate(self, instance)
Definition: listField.py:306
def validateItem(self, i, x)
Definition: listField.py:75
def __setitem__(self, i, x, at=None, label="setitem", setHistory=True)
Definition: listField.py:117
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
Definition: functional.cc:33
def getCallStack(skip=0)
Definition: callStack.py:169
def __get__(self, instance, owner=None, at=None, label="default")
Definition: config.py:453
def insert(self, i, x, at=None, label="insert", setHistory=True)
Definition: listField.py:154
def __init__(self, config, field, value, at, label, setHistory=True)
Definition: listField.py:59
def _setup(self, doc, dtype, default, check, optional, source)
Definition: config.py:264
def __setattr__(self, attr, value, at=None, label="assignment")
Definition: listField.py:198
def getStackFrame(relative=0)
Definition: callStack.py:52
def __delitem__(self, i, at=None, label="delitem", setHistory=True)
Definition: listField.py:141
def compareScalars(name, v1, v2, output, rtol=1E-8, atol=1E-8, dtype=None)
Definition: comparison.py:56
def __set__(self, instance, value, at=None, label="assignment")
Definition: listField.py:346
daf::base::PropertyList * list
Definition: fits.cc:833
def getComparisonName(name1, name2)
Definition: comparison.py:34