LSSTApplications  19.0.0-14-gb0260a2+72efe9b372,20.0.0+7927753e06,20.0.0+8829bf0056,20.0.0+995114c5d2,20.0.0+b6f4b2abd1,20.0.0+bddc4f4cbe,20.0.0-1-g253301a+8829bf0056,20.0.0-1-g2b7511a+0d71a2d77f,20.0.0-1-g5b95a8c+7461dd0434,20.0.0-12-g321c96ea+23efe4bbff,20.0.0-16-gfab17e72e+fdf35455f6,20.0.0-2-g0070d88+ba3ffc8f0b,20.0.0-2-g4dae9ad+ee58a624b3,20.0.0-2-g61b8584+5d3db074ba,20.0.0-2-gb780d76+d529cf1a41,20.0.0-2-ged6426c+226a441f5f,20.0.0-2-gf072044+8829bf0056,20.0.0-2-gf1f7952+ee58a624b3,20.0.0-20-geae50cf+e37fec0aee,20.0.0-25-g3dcad98+544a109665,20.0.0-25-g5eafb0f+ee58a624b3,20.0.0-27-g64178ef+f1f297b00a,20.0.0-3-g4cc78c6+e0676b0dc8,20.0.0-3-g8f21e14+4fd2c12c9a,20.0.0-3-gbd60e8c+187b78b4b8,20.0.0-3-gbecbe05+48431fa087,20.0.0-38-ge4adf513+a12e1f8e37,20.0.0-4-g97dc21a+544a109665,20.0.0-4-gb4befbc+087873070b,20.0.0-4-gf910f65+5d3db074ba,20.0.0-5-gdfe0fee+199202a608,20.0.0-5-gfbfe500+d529cf1a41,20.0.0-6-g64f541c+d529cf1a41,20.0.0-6-g9a5b7a1+a1cd37312e,20.0.0-68-ga3f3dda+5fca18c6a4,20.0.0-9-g4aef684+e18322736b,w.2020.45
LSSTDataManagementBasePackage
listField.py
Go to the documentation of this file.
1 # This file is part of pex_config.
2 #
3 # Developed for the LSST Data Management System.
4 # This product includes software developed by the LSST Project
5 # (http://www.lsst.org).
6 # See the COPYRIGHT file at the top-level directory of this distribution
7 # for details of code ownership.
8 #
9 # This software is dual licensed under the GNU General Public License and also
10 # under a 3-clause BSD license. Recipients may choose which of these licenses
11 # to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
12 # respectively. If you choose the GPL option then the following text applies
13 # (but note that there is still no warranty even if you opt for BSD instead):
14 #
15 # This program is free software: you can redistribute it and/or modify
16 # it under the terms of the GNU General Public License as published by
17 # the Free Software Foundation, either version 3 of the License, or
18 # (at your option) any later version.
19 #
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
24 #
25 # You should have received a copy of the GNU General Public License
26 # along with this program. If not, see <http://www.gnu.org/licenses/>.
27 
28 __all__ = ["ListField"]
29 
30 import collections.abc
31 
32 from .config import Field, FieldValidationError, _typeStr, _autocast, _joinNamePath
33 from .comparison import compareScalars, getComparisonName
34 from .callStack import getCallStack, getStackFrame
35 
36 
37 class List(collections.abc.MutableSequence):
38  """List collection used internally by `ListField`.
39 
40  Parameters
41  ----------
42  config : `lsst.pex.config.Config`
43  Config instance that contains the ``field``.
44  field : `ListField`
45  Instance of the `ListField` using this ``List``.
46  value : sequence
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`).
50  label : `str`
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`.
55 
56  Raises
57  ------
58  FieldValidationError
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.
62  """
63 
64  def __init__(self, config, field, value, at, label, setHistory=True):
65  self._field = field
66  self._config = config
67  self._history = self._config._history.setdefault(self._field.name, [])
68  self._list = []
69  self.__doc__ = field.doc
70  if value is not None:
71  try:
72  for i, x in enumerate(value):
73  self.insert(i, x, setHistory=False)
74  except TypeError:
75  msg = "Value %s is of incorrect type %s. Sequence type expected" % (value, _typeStr(value))
76  raise FieldValidationError(self._field, self._config, msg)
77  if setHistory:
78  self.history.append((list(self._list), at, label))
79 
80  def validateItem(self, i, x):
81  """Validate an item to determine if it can be included in the list.
82 
83  Parameters
84  ----------
85  i : `int`
86  Index of the item in the `list`.
87  x : object
88  Item in the `list`.
89 
90  Raises
91  ------
92  FieldValidationError
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.
96  """
97 
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))
101  raise FieldValidationError(self._field, self._config, msg)
102 
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)
105  raise FieldValidationError(self._field, self._config, msg)
106 
107  def list(self):
108  """Sequence of items contained by the `List` (`list`).
109  """
110  return self._list
111 
112  history = property(lambda x: x._history)
113  """Read-only history.
114  """
115 
116  def __contains__(self, x):
117  return x in self._list
118 
119  def __len__(self):
120  return len(self._list)
121 
122  def __setitem__(self, i, x, at=None, label="setitem", setHistory=True):
123  if self._config._frozen:
124  raise FieldValidationError(self._field, self._config,
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)
130  self.validateItem(k, xj)
131  x[j] = xj
132  k += step
133  else:
134  x = _autocast(x, self._field.itemtype)
135  self.validateItem(i, x)
136 
137  self._list[i] = x
138  if setHistory:
139  if at is None:
140  at = getCallStack()
141  self.history.append((list(self._list), at, label))
142 
143  def __getitem__(self, i):
144  return self._list[i]
145 
146  def __delitem__(self, i, at=None, label="delitem", setHistory=True):
147  if self._config._frozen:
148  raise FieldValidationError(self._field, self._config,
149  "Cannot modify a frozen Config")
150  del self._list[i]
151  if setHistory:
152  if at is None:
153  at = getCallStack()
154  self.history.append((list(self._list), at, label))
155 
156  def __iter__(self):
157  return iter(self._list)
158 
159  def insert(self, i, x, at=None, label="insert", setHistory=True):
160  """Insert an item into the list at the given index.
161 
162  Parameters
163  ----------
164  i : `int`
165  Index where the item is inserted.
166  x : object
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`.
176  """
177  if at is None:
178  at = getCallStack()
179  self.__setitem__(slice(i, i), [x], at=at, label=label, setHistory=setHistory)
180 
181  def __repr__(self):
182  return repr(self._list)
183 
184  def __str__(self):
185  return str(self._list)
186 
187  def __eq__(self, other):
188  try:
189  if len(self) != len(other):
190  return False
191 
192  for i, j in zip(self, other):
193  if i != j:
194  return False
195  return True
196  except AttributeError:
197  # other is not a sequence type
198  return False
199 
200  def __ne__(self, other):
201  return not self.__eq__(other)
202 
203  def __setattr__(self, attr, value, at=None, label="assignment"):
204  if hasattr(getattr(self.__class__, attr, None), '__set__'):
205  # This allows properties to work.
206  object.__setattr__(self, attr, value)
207  elif attr in self.__dict__ or attr in ["_field", "_config", "_history", "_list", "__doc__"]:
208  # This allows specific private attributes to work.
209  object.__setattr__(self, attr, value)
210  else:
211  # We throw everything else.
212  msg = "%s has no attribute %s" % (_typeStr(self._field), attr)
213  raise FieldValidationError(self._field, self._config, msg)
214 
215 
217  """A configuration field (`~lsst.pex.config.Field` subclass) that contains
218  a list of values of a specific type.
219 
220  Parameters
221  ----------
222  doc : `str`
223  A description of the field.
224  dtype : class
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
231  `None`.
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
240  items.
241  maxLength : `int`, optional
242  If set, this field must contain *no more than* ``maxLength`` number of
243  items.
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.
247 
248  See also
249  --------
250  ChoiceField
251  ConfigChoiceField
252  ConfigDictField
253  ConfigField
254  ConfigurableField
255  DictField
256  Field
257  RangeField
258  RegistryField
259  """
260  def __init__(self, doc, dtype, default=None, optional=False,
261  listCheck=None, itemCheck=None,
262  length=None, minLength=None, maxLength=None,
263  deprecated=None):
264  if dtype not in Field.supportedTypes:
265  raise ValueError("Unsupported dtype %s" % _typeStr(dtype))
266  if length is not None:
267  if length <= 0:
268  raise ValueError("'length' (%d) must be positive" % length)
269  minLength = None
270  maxLength = None
271  else:
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))
278 
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")
283 
284  source = getStackFrame()
285  self._setup(doc=doc, dtype=List, default=default, check=None, optional=optional, source=source,
286  deprecated=deprecated)
287 
288  self.listCheck = listCheck
289  """Callable used to check the list as a whole.
290  """
291 
292  self.itemCheck = itemCheck
293  """Callable used to validate individual items as they are inserted
294  into the list.
295  """
296 
297  self.itemtype = dtype
298  """Data type of list items.
299  """
300 
301  self.length = length
302  """Number of items that must be present in the list (or `None` to
303  disable checking the list's length).
304  """
305 
306  self.minLength = minLength
307  """Minimum number of items that must be present in the list (or `None`
308  to disable checking the list's minimum length).
309  """
310 
311  self.maxLength = maxLength
312  """Maximum number of items that must be present in the list (or `None`
313  to disable checking the list's maximum length).
314  """
315 
316  def validate(self, instance):
317  """Validate the field.
318 
319  Parameters
320  ----------
321  instance : `lsst.pex.config.Config`
322  The config instance that contains this field.
323 
324  Raises
325  ------
326  lsst.pex.config.FieldValidationError
327  Raised if:
328 
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`.
333 
334  Notes
335  -----
336  Individual item checks (`itemCheck`) are applied when each item is
337  set and are not re-checked by this method.
338  """
339  Field.validate(self, instance)
340  value = self.__get__(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)
345  raise FieldValidationError(self, instance, msg)
346  elif self.minLength is not None and lenValue < self.minLength:
347  msg = "Minimum allowed list length=%d, got length=%d" % (self.minLength, lenValue)
348  raise FieldValidationError(self, instance, msg)
349  elif self.maxLength is not None and lenValue > self.maxLength:
350  msg = "Maximum allowed list length=%d, got length=%d" % (self.maxLength, lenValue)
351  raise FieldValidationError(self, instance, msg)
352  elif self.listCheck is not None and not self.listCheck(value):
353  msg = "%s is not a valid value" % str(value)
354  raise FieldValidationError(self, instance, msg)
355 
356  def __set__(self, instance, value, at=None, label="assignment"):
357  if instance._frozen:
358  raise FieldValidationError(self, instance, "Cannot modify a frozen Config")
359 
360  if at is None:
361  at = getCallStack()
362 
363  if value is not None:
364  value = List(instance, self, value, at, label)
365  else:
366  history = instance._history.setdefault(self.name, [])
367  history.append((value, at, label))
368 
369  instance._storage[self.name] = value
370 
371  def toDict(self, instance):
372  """Convert the value of this field to a plain `list`.
373 
374  `lsst.pex.config.Config.toDict` is the primary user of this method.
375 
376  Parameters
377  ----------
378  instance : `lsst.pex.config.Config`
379  The config instance that contains this field.
380 
381  Returns
382  -------
383  `list`
384  Plain `list` of items, or `None` if the field is not set.
385  """
386  value = self.__get__(instance)
387  return list(value) if value is not None else None
388 
389  def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
390  """Compare two config instances for equality with respect to this
391  field.
392 
393  `lsst.pex.config.config.compare` is the primary user of this method.
394 
395  Parameters
396  ----------
397  instance1 : `lsst.pex.config.Config`
398  Left-hand-side `~lsst.pex.config.Config` instance in the
399  comparison.
400  instance2 : `lsst.pex.config.Config`
401  Right-hand-side `~lsst.pex.config.Config` instance in the
402  comparison.
403  shortcut : `bool`
404  If `True`, return as soon as an **inequality** is found.
405  rtol : `float`
406  Relative tolerance for floating point comparisons.
407  atol : `float`
408  Absolute tolerance for floating point comparisons.
409  output : callable
410  If not None, a callable that takes a `str`, used (possibly
411  repeatedly) to report inequalities.
412 
413  Returns
414  -------
415  equal : `bool`
416  `True` if the fields are equal; `False` otherwise.
417 
418  Notes
419  -----
420  Floating point comparisons are performed by `numpy.allclose`.
421  """
422  l1 = getattr(instance1, self.name)
423  l2 = getattr(instance2, self.name)
424  name = getComparisonName(
425  _joinNamePath(instance1._name, self.name),
426  _joinNamePath(instance2._name, self.name)
427  )
428  if not compareScalars("isnone for %s" % name, l1 is None, l2 is None, output=output):
429  return False
430  if l1 is None and l2 is None:
431  return True
432  if not compareScalars("size for %s" % name, len(l1), len(l2), output=output):
433  return False
434  equal = True
435  for n, v1, v2 in zip(range(len(l1)), l1, l2):
436  result = compareScalars("%s[%d]" % (name, n), v1, v2, dtype=self.dtype,
437  rtol=rtol, atol=atol, output=output)
438  if not result and shortcut:
439  return False
440  equal = equal and result
441  return equal
lsst.pex.config.listField.List.__contains__
def __contains__(self, x)
Definition: listField.py:116
lsst.pex.config.comparison.compareScalars
def compareScalars(name, v1, v2, output, rtol=1E-8, atol=1E-8, dtype=None)
Definition: comparison.py:62
lsst.pex.config.listField.List._config
_config
Definition: listField.py:66
lsst.pex.config.listField.List._field
_field
Definition: listField.py:65
lsst.pex.config.listField.ListField.itemtype
itemtype
Definition: listField.py:294
lsst.pex.config.listField.List.history
history
Definition: listField.py:112
lsst.pex.config.listField.ListField
Definition: listField.py:216
lsst.pex.config.listField.ListField.validate
def validate(self, instance)
Definition: listField.py:316
lsst.pex.config.listField.List
Definition: listField.py:37
ast::append
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
Definition: functional.cc:33
lsst.pex.config.listField.List.insert
def insert(self, i, x, at=None, label="insert", setHistory=True)
Definition: listField.py:159
lsst.pex.config.listField.ListField.itemCheck
itemCheck
Definition: listField.py:289
lsst.pex.config.listField.ListField.minLength
minLength
Definition: listField.py:303
lsst.pex.config.listField.List.__iter__
def __iter__(self)
Definition: listField.py:156
lsst.pex.config.listField.List.__setitem__
def __setitem__(self, i, x, at=None, label="setitem", setHistory=True)
Definition: listField.py:122
lsst.pex.config.listField.List.__repr__
def __repr__(self)
Definition: listField.py:181
lsst.pex.config.listField.List.__getitem__
def __getitem__(self, i)
Definition: listField.py:143
lsst.pex.config.callStack.getCallStack
def getCallStack(skip=0)
Definition: callStack.py:175
lsst.pex.config.listField.List.__delitem__
def __delitem__(self, i, at=None, label="delitem", setHistory=True)
Definition: listField.py:146
lsst.pex.config.listField.List.__setattr__
def __setattr__(self, attr, value, at=None, label="assignment")
Definition: listField.py:203
lsst.pex.config.config.FieldValidationError
Definition: config.py:196
lsst.pex.config.listField.ListField.listCheck
listCheck
Definition: listField.py:285
lsst.pex.config.listField.List._history
_history
Definition: listField.py:67
lsst.pex.config.listField.List.validateItem
def validateItem(self, i, x)
Definition: listField.py:80
lsst.pex.config.listField.List.__ne__
def __ne__(self, other)
Definition: listField.py:200
lsst.pex.config.listField.List.__len__
def __len__(self)
Definition: listField.py:119
lsst.pex.config.config.Field.dtype
dtype
Definition: config.py:339
lsst.pex.config.listField.List.__str__
def __str__(self)
Definition: listField.py:184
lsst.pex.config.config.Field.__get__
def __get__(self, instance, owner=None, at=None, label="default")
Definition: config.py:550
lsst.pex.config.listField.ListField.toDict
def toDict(self, instance)
Definition: listField.py:371
lsst.pex.config.listField.ListField.length
length
Definition: listField.py:298
list
daf::base::PropertyList * list
Definition: fits.cc:913
lsst.pex.config.listField.List.list
def list(self)
Definition: listField.py:107
lsst.pex.config.listField.ListField.maxLength
maxLength
Definition: listField.py:308
lsst.pex.config.callStack.getStackFrame
def getStackFrame(relative=0)
Definition: callStack.py:58
lsst.pex.config.config.Field
Definition: config.py:247
lsst.pex.config.listField.ListField.__set__
def __set__(self, instance, value, at=None, label="assignment")
Definition: listField.py:356
lsst.pex.config.listField.List.__init__
def __init__(self, config, field, value, at, label, setHistory=True)
Definition: listField.py:64
lsst.pex.config.listField.List.__doc__
__doc__
Definition: listField.py:69
lsst.pex.config.listField.List._list
_list
Definition: listField.py:68
astshim.fitsChanContinued.iter
def iter(self)
Definition: fitsChanContinued.py:88
lsst.pex.config.listField.List.__eq__
def __eq__(self, other)
Definition: listField.py:187
lsst.pex.config.comparison.getComparisonName
def getComparisonName(name1, name2)
Definition: comparison.py:40
lsst.pex.config.listField.ListField.__init__
def __init__(self, doc, dtype, default=None, optional=False, listCheck=None, itemCheck=None, length=None, minLength=None, maxLength=None, deprecated=None)
Definition: listField.py:260
lsst.pex.config.config.Field._setup
def _setup(self, doc, dtype, default, check, optional, source, deprecated)
Definition: config.py:336