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
propertyContainerContinued.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 #
4 # Copyright 2008-2017 AURA/LSST.
5 #
6 # This product includes software developed by the
7 # LSST Project (http://www.lsst.org/).
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the LSST License Statement and
20 # the GNU General Public License along with this program. If not,
21 # see <http://www.lsstcorp.org/LegalNotices/>.
22 #
23 
24 
25 __all__ = ["getPropertySetState", "getPropertyListState", "setPropertySetState", "setPropertyListState"]
26 
27 import enum
28 import numbers
29 from collections.abc import Mapping, KeysView, ValuesView, ItemsView
30 
31 # Ensure that C++ exceptions are properly translated to Python
32 import lsst.pex.exceptions # noqa: F401
33 from lsst.utils import continueClass
34 
35 from .propertySet import PropertySet
36 from .propertyList import PropertyList
37 from ..dateTime import DateTime
38 
39 
40 def getPropertySetState(container, asLists=False):
41  """Get the state of a PropertySet in a form that can be pickled.
42 
43  Parameters
44  ----------
45  container : `PropertySet`
46  The property container.
47  asLists : `bool`, optional
48  If False, the default, `tuple` will be used for the contents. If true
49  a `list` will be used.
50 
51  Returns
52  -------
53  state : `list` of `tuple` or `list` of `list`
54  The state, as a list of tuples (or lists), each of which contains
55  the following 3 items:
56 
57  name (a `str`)
58  the name of the item
59  elementTypeName (a `str`)
60  the suffix of a ``setX`` method name
61  which is appropriate for the data type. For example integer
62  data has ``elementTypeName="Int"` which corresponds to
63  the ``setInt`` method.
64  value
65  the data for the item, in a form compatible
66  with the set method named by ``elementTypeName``
67  """
68  names = container.names(topLevelOnly=True)
69  sequence = list if asLists else tuple
70  return [sequence((name, _propertyContainerElementTypeName(container, name),
71  _propertyContainerGet(container, name, returnStyle=ReturnStyle.AUTO)))
72  for name in names]
73 
74 
75 def getPropertyListState(container, asLists=False):
76  """Get the state of a PropertyList in a form that can be pickled.
77 
78  Parameters
79  ----------
80  container : `PropertyList`
81  The property container.
82  asLists : `bool`, optional
83  If False, the default, `tuple` will be used for the contents. If true
84  a `list` will be used.
85 
86  Returns
87  -------
88  state : `list` of `tuple` or `list` of `list`
89  The state, as a list of tuples (or lists), each of which contains
90  the following 4 items:
91 
92  name (a `str`):
93  the name of the item
94  elementTypeName (a `str`):
95  the suffix of a ``setX`` method name
96  which is appropriate for the data type. For example integer
97  data has ``elementTypeName="Int"` which corresponds to
98  the ``setInt`` method.
99  value
100  the data for the item, in a form compatible
101  with the set method named by ``elementTypeName``
102  comment (a `str`): the comment. This item is only present
103  if ``container`` is a PropertyList.
104  """
105  sequence = list if asLists else tuple
106  return [sequence((name, _propertyContainerElementTypeName(container, name),
107  _propertyContainerGet(container, name, returnStyle=ReturnStyle.AUTO),
108  container.getComment(name)))
109  for name in container.getOrderedNames()]
110 
111 
112 def setPropertySetState(container, state):
113  """Restore the state of a PropertySet, in place.
114 
115  Parameters
116  ----------
117  container : `PropertySet`
118  The property container whose state is to be restored.
119  It should be empty to start with and is updated in place.
120  state : `list`
121  The state, as returned by `getPropertySetState`
122  """
123  for name, elemType, value in state:
124  if elemType is not None:
125  getattr(container, "set" + elemType)(name, value)
126  else:
127  raise ValueError(f"Unrecognized values for state restoration: ({name}, {elemType}, {value})")
128 
129 
130 def setPropertyListState(container, state):
131  """Restore the state of a PropertyList, in place.
132 
133  Parameters
134  ----------
135  container : `PropertyList`
136  The property container whose state is to be restored.
137  It should be empty to start with and is updated in place.
138  state : `list`
139  The state, as returned by ``getPropertyListState``
140  """
141  for name, elemType, value, comment in state:
142  getattr(container, "set" + elemType)(name, value, comment)
143 
144 
145 class ReturnStyle(enum.Enum):
146  ARRAY = enum.auto()
147  SCALAR = enum.auto()
148  AUTO = enum.auto()
149 
150 
151 def _propertyContainerElementTypeName(container, name):
152  """Return name of the type of a particular element
153 
154  Parameters
155  ----------
156  container : `lsst.daf.base.PropertySet` or `lsst.daf.base.PropertyList`
157  Container including the element
158  name : `str`
159  Name of element
160  """
161  try:
162  t = container.typeOf(name)
163  except LookupError as e:
164  # KeyError is more commonly expected when asking for an element
165  # from a mapping.
166  raise KeyError(str(e))
167  for checkType in ("Bool", "Short", "Int", "Long", "LongLong", "UnsignedLongLong",
168  "Float", "Double", "String", "DateTime",
169  "PropertySet", "Undef"):
170  if t == getattr(container, "TYPE_" + checkType):
171  return checkType
172  return None
173 
174 
175 def _propertyContainerGet(container, name, returnStyle):
176  """Get a value of unknown type as a scalar or array
177 
178  Parameters
179  ----------
180  container : `lsst.daf.base.PropertySet` or `lsst.daf.base.PropertyList`
181  Container from which to get the value
182  name : `str`
183  Name of item
184  returnStyle : `ReturnStyle`
185  Control whether numeric or string data is returned as an array
186  or scalar (the other types, ``PropertyList``, ``PropertySet``
187  and ``PersistablePtr``, are always returned as a scalar):
188  - ReturnStyle.ARRAY: return numeric or string data types
189  as an array of values.
190  - ReturnStyle.SCALAR: return numeric or string data types
191  as a single value; if the item has multiple values then
192  return the last value.
193  - ReturnStyle.AUTO: (deprecated) return numeric or string data
194  as a scalar if there is just one item, or as an array
195  otherwise.
196 
197  Raises
198  ------
199  KeyError
200  Raised if the specified key does not exist in the container.
201  TypeError
202  Raised if the value retrieved is of an unexpected type.
203  ValueError
204  Raised if the value for ``returnStyle`` is not correct.
205  """
206  if not container.exists(name):
207  raise KeyError(name + " not found")
208  if returnStyle not in ReturnStyle:
209  raise ValueError("returnStyle {} must be a ReturnStyle".format(returnStyle))
210 
211  elemType = _propertyContainerElementTypeName(container, name)
212  if elemType and elemType != "PropertySet":
213  value = getattr(container, "getArray" + elemType)(name)
214  if returnStyle == ReturnStyle.ARRAY or (returnStyle == ReturnStyle.AUTO and len(value) > 1):
215  return value
216  return value[-1]
217 
218  if container.isPropertySetPtr(name):
219  try:
220  return container.getAsPropertyListPtr(name)
221  except Exception:
222  return container.getAsPropertySetPtr(name)
223  try:
224  return container.getAsPersistablePtr(name)
225  except Exception:
226  pass
227  raise TypeError('Unknown PropertySet value type for ' + name)
228 
229 
230 def _guessIntegerType(container, name, value):
231  """Given an existing container and name, determine the type
232  that should be used for the supplied value. The supplied value
233  is assumed to be a scalar.
234 
235  On Python 3 all ints are LongLong but we need to be able to store them
236  in Int containers if that is what is being used (testing for truncation).
237  Int is assumed to mean 32bit integer (2147483647 to -2147483648).
238 
239  If there is no pre-existing value we have to decide what to do. For now
240  we pick Int if the value is less than maxsize.
241 
242  Parameters
243  ----------
244  container : `lsst.daf.base.PropertySet` or `lsst.daf.base.PropertyList`
245  Container from which to get the value
246 
247  name : `str`
248  Name of item
249 
250  value : `object`
251  Value to be assigned a type
252 
253  Returns
254  -------
255  useType : `str` or none
256  Type to use for the supplied value. `None` if the input is
257  `bool` or a non-integral value.
258  """
259  useType = None
260  maxInt = 2147483647
261  minInt = -2147483648
262  maxLongLong = 2**63 - 1
263  minLongLong = -2**63
264  maxU64 = 2**64 - 1
265  minU64 = 0
266 
267  # We do not want to convert bool to int so let the system work that
268  # out itself
269  if isinstance(value, bool):
270  return useType
271 
272  if isinstance(value, numbers.Integral):
273  try:
274  containerType = _propertyContainerElementTypeName(container, name)
275  except LookupError:
276  # nothing in the container so choose based on size.
277  if value <= maxInt and value >= minInt:
278  useType = "Int"
279  elif value <= maxLongLong and value >= minLongLong:
280  useType = "LongLong"
281  elif value <= maxU64 and value >= minU64:
282  useType = "UnsignedLongLong"
283  else:
284  raise RuntimeError("Unable to guess integer type for storing value: %d" % (value,))
285  else:
286  if containerType == "Int":
287  # Always use an Int even if we know it won't fit. The later
288  # code will trigger OverflowError if appropriate. Setting the
289  # type to LongLong here will trigger a TypeError instead so
290  # it's best to trigger a predictable OverflowError.
291  useType = "Int"
292  elif containerType == "LongLong":
293  useType = "LongLong"
294  elif containerType == "UnsignedLongLong":
295  useType = "UnsignedLongLong"
296  return useType
297 
298 
299 def _propertyContainerSet(container, name, value, typeMenu, *args):
300  """Set a single Python value of unknown type
301  """
302  if hasattr(value, "__iter__") and not isinstance(value, (str, PropertySet, PropertyList)):
303  exemplar = value[0]
304  else:
305  exemplar = value
306 
307  t = type(exemplar)
308  setType = _guessIntegerType(container, name, exemplar)
309 
310  if setType is not None or t in typeMenu:
311  if setType is None:
312  setType = typeMenu[t]
313  return getattr(container, "set" + setType)(name, value, *args)
314  # Allow for subclasses
315  for checkType in typeMenu:
316  if (checkType is None and exemplar is None) or \
317  (checkType is not None and isinstance(exemplar, checkType)):
318  return getattr(container, "set" + typeMenu[checkType])(name, value, *args)
319  raise TypeError("Unknown value type for key '%s': %s" % (name, t))
320 
321 
322 def _propertyContainerAdd(container, name, value, typeMenu, *args):
323  """Add a single Python value of unknown type
324  """
325  if hasattr(value, "__iter__"):
326  exemplar = value[0]
327  else:
328  exemplar = value
329 
330  t = type(exemplar)
331  addType = _guessIntegerType(container, name, exemplar)
332 
333  if addType is not None or t in typeMenu:
334  if addType is None:
335  addType = typeMenu[t]
336  return getattr(container, "add" + addType)(name, value, *args)
337  # Allow for subclasses
338  for checkType in typeMenu:
339  if (checkType is None and exemplar is None) or \
340  (checkType is not None and isinstance(exemplar, checkType)):
341  return getattr(container, "add" + typeMenu[checkType])(name, value, *args)
342  raise TypeError("Unknown value type for key '%s': %s" % (name, t))
343 
344 
345 def _makePropertySet(state):
346  """Make a `PropertySet` from the state returned by `getPropertySetState`
347 
348  Parameters
349  ----------
350  state : `list`
351  The data returned by `getPropertySetState`.
352  """
353  ps = PropertySet()
354  setPropertySetState(ps, state)
355  return ps
356 
357 
358 def _makePropertyList(state):
359  """Make a `PropertyList` from the state returned by
360  `getPropertyListState`
361 
362  Parameters
363  ----------
364  state : `list`
365  The data returned by `getPropertySetState`.
366  """
367  pl = PropertyList()
368  setPropertyListState(pl, state)
369  return pl
370 
371 
372 @continueClass
374  # Mapping of type to method names;
375  # int types are omitted due to use of _guessIntegerType
376  _typeMenu = {bool: "Bool",
377  float: "Double",
378  str: "String",
379  DateTime: "DateTime",
380  PropertySet: "PropertySet",
381  PropertyList: "PropertySet",
382  None: "Undef",
383  }
384 
385  def get(self, name, default=None):
386  """Return an item as a scalar, else default.
387 
388  Identical to `getScalar` except that a default value is returned
389  if the requested key is not present. If an array item is requested
390  the final value in the array will be returned.
391 
392  Parameters
393  ----------
394  name : `str`
395  Name of item
396  default : `object`, optional
397  Default value to use if the named item is not present.
398 
399  Returns
400  -------
401  value : any type supported by container
402  Single value of any type supported by the container, else the
403  default value if the requested item is not present in the
404  container. For array items the most recently added value is
405  returned.
406  """
407  try:
408  return _propertyContainerGet(self, name, returnStyle=ReturnStyle.SCALAR)
409  except KeyError:
410  return default
411 
412  def getArray(self, name):
413  """Return an item as an array if the item is numeric or string
414 
415  If the item is a `PropertySet`, `PropertyList` or
416  `lsst.daf.base.PersistablePtr` then return the item as a scalar.
417 
418  Parameters
419  ----------
420  name : `str`
421  Name of item
422 
423  Returns
424  -------
425  values : `list` of any type supported by container
426  The contents of the item, guaranteed to be returned as a `list.`
427 
428  Raises
429  ------
430  KeyError
431  Raised if the item does not exist.
432  """
433  return _propertyContainerGet(self, name, returnStyle=ReturnStyle.ARRAY)
434 
435  def getScalar(self, name):
436  """Return an item as a scalar
437 
438  If the item has more than one value then the last value is returned.
439 
440  Parameters
441  ----------
442  name : `str`
443  Name of item
444 
445  Returns
446  -------
447  value : scalar item
448  Value stored in the item. If the item refers to an array the
449  most recently added value is returned.
450 
451  Raises
452  ------
453  KeyError
454  Raised if the item does not exist.
455  """
456  return _propertyContainerGet(self, name, returnStyle=ReturnStyle.SCALAR)
457 
458  def set(self, name, value):
459  """Set the value of an item
460 
461  If the item already exists it is silently replaced; the types
462  need not match.
463 
464  Parameters
465  ----------
466  name : `str`
467  Name of item
468  value : any supported type
469  Value of item; may be a scalar or array
470  """
471  return _propertyContainerSet(self, name, value, self._typeMenu)
472 
473  def add(self, name, value):
474  """Append one or more values to a given item, which need not exist
475 
476  If the item exists then the new value(s) are appended;
477  otherwise it is like calling `set`
478 
479  Parameters
480  ----------
481  name : `str`
482  Name of item
483  value : any supported type
484  Value of item; may be a scalar or array
485 
486  Notes
487  -----
488  If ``value`` is an `lsst.daf.base.PropertySet` or
489  `lsst.daf.base.PropertyList` then ``value`` replaces
490  the existing value. Also the item is added as a live
491  reference, so updating ``value`` will update this container
492  and vice-versa.
493 
494  Raises
495  ------
496  lsst::pex::exceptions::TypeError
497  Raised if the type of `value` is incompatible with the existing
498  value of the item.
499  """
500  return _propertyContainerAdd(self, name, value, self._typeMenu)
501 
502  def update(self, addition):
503  """Update the current container with the supplied additions.
504 
505  Parameters
506  ----------
507  addition : `collections.abc.Mapping` or `PropertySet`
508  The content to merge into the current container.
509 
510  Notes
511  -----
512  This is not the same as calling `PropertySet.combine` since the
513  behavior differs when both mappings contain the same key. This
514  method updates by overwriting existing values completely with
515  the new value.
516  """
517  if isinstance(addition, PropertySet):
518  # To support array values we can not use the dict interface
519  # and instead use the copy() method which overwrites
520  for k in addition:
521  self.copy(k, addition, k)
522  else:
523  for k, v in addition.items():
524  self[k] = v
525 
526  def toDict(self):
527  """Returns a (possibly nested) dictionary with all properties.
528 
529  Returns
530  -------
531  d : `dict`
532  Dictionary with all names and values (no comments).
533  """
534 
535  d = {}
536  for name in self.names():
537  v = _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO)
538 
539  if isinstance(v, PropertySet):
540  d[name] = PropertySet.toDict(v)
541  else:
542  d[name] = v
543  return d
544 
545  def __eq__(self, other):
546  if type(self) != type(other):
547  return False
548 
549  if len(self) != len(other):
550  return False
551 
552  for name in self:
553  if _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO) != \
554  _propertyContainerGet(other, name, returnStyle=ReturnStyle.AUTO):
555  return False
556  if self.typeOf(name) != other.typeOf(name):
557  return False
558 
559  return True
560 
561  def __copy__(self):
562  # Copy without having to go through pickle state
563  ps = PropertySet()
564  for itemName in self:
565  ps.copy(itemName, self, itemName)
566  return ps
567 
568  def __deepcopy__(self, memo):
569  result = self.deepCopy()
570  memo[id(self)] = result
571  return result
572 
573  def __contains__(self, name):
574  """Determines if the name is found at the top level hierarchy
575  of the container.
576 
577  Notes
578  ------
579  Does not use `PropertySet.exists()`` because that includes support
580  for "."-delimited names. This method is consistent with the
581  items returned from ``__iter__``.
582  """
583  return name in self.names(topLevelOnly=True)
584 
585  def __setitem__(self, name, value):
586  """Assigns the supplied value to the container.
587 
588  Parameters
589  ----------
590  name : `str`
591  Name of item to update.
592  value : Value to assign
593  Can be any value supported by the container's ``set()``
594  method. `~collections.abc.Mapping` are converted to
595  `PropertySet` before assignment.
596 
597  Notes
598  -----
599  Uses `PropertySet.set`, overwriting any previous value.
600  """
601  if isinstance(value, Mapping):
602  # Create a property set instead
603  ps = PropertySet()
604  for k, v in value.items():
605  ps[k] = v
606  value = ps
607  self.set(name, value)
608 
609  def __getitem__(self, name):
610  """Returns a scalar item from the container.
611 
612  Notes
613  -----
614  Uses `PropertySet.getScalar` to guarantee that a single value
615  will be returned.
616  """
617  return self.getScalar(name)
618 
619  def __delitem__(self, name):
620  if name in self:
621  self.remove(name)
622  else:
623  raise KeyError(f"{name} not present in dict")
624 
625  def __str__(self):
626  return self.toString()
627 
628  def __len__(self):
629  return self.nameCount(topLevelOnly=True)
630 
631  def __iter__(self):
632  for n in self.names(topLevelOnly=True):
633  yield n
634 
635  def keys(self):
636  return KeysView(self)
637 
638  def items(self):
639  return ItemsView(self)
640 
641  def values(self):
642  return ValuesView(self)
643 
644  def __reduce__(self):
645  # It would be a bit simpler to use __setstate__ and __getstate__.
646  # However, implementing __setstate__ in Python causes segfaults
647  # because pickle creates a new instance by calling
648  # object.__new__(PropertyList, *args) which bypasses
649  # the pybind11 memory allocation step.
650  return (_makePropertySet, (getPropertySetState(self),))
651 
652 
653 @continueClass
655  # Mapping of type to method names
656  _typeMenu = {bool: "Bool",
657  int: "Int",
658  float: "Double",
659  str: "String",
660  DateTime: "DateTime",
661  PropertySet: "PropertySet",
662  PropertyList: "PropertySet",
663  None: "Undef",
664  }
665 
666  COMMENTSUFFIX = "#COMMENT"
667  """Special suffix used to indicate that a named item being assigned
668  using dict syntax is referring to a comment, not value."""
669 
670  def get(self, name, default=None):
671  """Return an item as a scalar, else default.
672 
673  Identical to `getScalar` except that a default value is returned
674  if the requested key is not present. If an array item is requested
675  the final value in the array will be returned.
676 
677  Parameters
678  ----------
679  name : ``str``
680  Name of item
681  default : `object`, optional
682  Default value to use if the named item is not present.
683 
684  Returns
685  -------
686  value : any type supported by container
687  Single value of any type supported by the container, else the
688  default value if the requested item is not present in the
689  container. For array items the most recently added value is
690  returned.
691  """
692  try:
693  return _propertyContainerGet(self, name, returnStyle=ReturnStyle.SCALAR)
694  except KeyError:
695  return default
696 
697  def getArray(self, name):
698  """Return an item as a list.
699 
700  Parameters
701  ----------
702  name : `str`
703  Name of item
704 
705  Returns
706  -------
707  values : `list` of values
708  The contents of the item, guaranteed to be returned as a `list.`
709 
710  Raises
711  ------
712  KeyError
713  Raised if the item does not exist.
714  """
715  return _propertyContainerGet(self, name, returnStyle=ReturnStyle.ARRAY)
716 
717  def getScalar(self, name):
718  """Return an item as a scalar
719 
720  If the item has more than one value then the last value is returned.
721 
722  Parameters
723  ----------
724  name : `str`
725  Name of item.
726 
727  Returns
728  -------
729  value : scalar item
730  Value stored in the item. If the item refers to an array the
731  most recently added value is returned.
732 
733  Raises
734  ------
735  KeyError
736  Raised if the item does not exist.
737  """
738  return _propertyContainerGet(self, name, returnStyle=ReturnStyle.SCALAR)
739 
740  def set(self, name, value, comment=None):
741  """Set the value of an item
742 
743  If the item already exists it is silently replaced; the types
744  need not match.
745 
746  Parameters
747  ----------
748  name : `str`
749  Name of item
750  value : any supported type
751  Value of item; may be a scalar or array
752  """
753  args = []
754  if comment is not None:
755  args.append(comment)
756  return _propertyContainerSet(self, name, value, self._typeMenu, *args)
757 
758  def add(self, name, value, comment=None):
759  """Append one or more values to a given item, which need not exist
760 
761  If the item exists then the new value(s) are appended;
762  otherwise it is like calling `set`
763 
764  Parameters
765  ----------
766  name : `str`
767  Name of item
768  value : any supported type
769  Value of item; may be a scalar or array
770 
771  Notes
772  -----
773  If `value` is an `lsst.daf.base.PropertySet` items are added
774  using dotted names (e.g. if name="a" and value contains
775  an item "b" which is another PropertySet and contains an
776  item "c" which is numeric or string, then the value of "c"
777  is added as "a.b.c", appended to the existing values of
778  "a.b.c" if any (in which case the types must be compatible).
779 
780  Raises
781  ------
782  lsst::pex::exceptions::TypeError
783  Raise if the type of ``value`` is incompatible with the existing
784  value of the item.
785  """
786  args = []
787  if comment is not None:
788  args.append(comment)
789  return _propertyContainerAdd(self, name, value, self._typeMenu, *args)
790 
791  def setComment(self, name, comment):
792  """Set the comment for an existing entry.
793 
794  Parameters
795  ----------
796  name : `str`
797  Name of the key to receive updated comment.
798  comment : `comment`
799  New comment string.
800  """
801  # The only way to do this is to replace the existing entry with
802  # one that has the new comment
803  containerType = _propertyContainerElementTypeName(self, name)
804  if self.isArray(name):
805  value = self.getArray(name)
806  else:
807  value = self.getScalar(name)
808  getattr(self, f"set{containerType}")(name, value, comment)
809 
810  def toList(self):
811  """Return a list of tuples of name, value, comment for each property
812  in the order that they were inserted.
813 
814  Returns
815  -------
816  ret : `list` of `tuple`
817  Tuples of name, value, comment for each property in the order
818  in which they were inserted.
819  """
820  orderedNames = self.getOrderedNames()
821  ret = []
822  for name in orderedNames:
823  if self.isArray(name):
824  values = _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO)
825  for v in values:
826  ret.append((name, v, self.getComment(name)))
827  else:
828  ret.append((name, _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO),
829  self.getComment(name)))
830  return ret
831 
832  def toOrderedDict(self):
833  """Return an ordered dictionary with all properties in the order that
834  they were inserted.
835 
836  Returns
837  -------
838  d : `dict`
839  Ordered dictionary with all properties in the order that they
840  were inserted. Comments are not included.
841 
842  Notes
843  -----
844  As of Python 3.6 dicts retain their insertion order.
845  """
846  d = {}
847  for name in self:
848  d[name] = _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO)
849  return d
850 
851  # For PropertyList the two are equivalent
852  toDict = toOrderedDict
853 
854  def __eq__(self, other):
855  # super() doesn't seem to work properly in @continueClass;
856  # note that super with arguments seems to work at first, but actually
857  # doesn't either.
858  if not PropertySet.__eq__(self, other):
859  return False
860 
861  for name in self:
862  if self.getComment(name) != other.getComment(name):
863  return False
864 
865  return True
866 
867  def __copy__(self):
868  # Copy without having to go through pickle state
869  pl = PropertyList()
870  for itemName in self:
871  pl.copy(itemName, self, itemName)
872  return pl
873 
874  def __deepcopy__(self, memo):
875  result = self.deepCopy()
876  memo[id(self)] = result
877  return result
878 
879  def __iter__(self):
880  for n in self.getOrderedNames():
881  yield n
882 
883  def __setitem__(self, name, value):
884  """Assigns the supplied value to the container.
885 
886  Parameters
887  ----------
888  name : `str`
889  Name of item to update. If the name ends with
890  `PropertyList.COMMENTSUFFIX`, the comment is updated rather
891  than the value.
892  value : Value to assign
893  Can be any value supported by the container's ``set()``
894  method. `~collections.abc.Mapping` are converted to
895  `PropertySet` before assignment.
896 
897  Notes
898  -----
899  Uses `PropertySet.set`, overwriting any previous value.
900  """
901  if name.endswith(self.COMMENTSUFFIX):
902  name = name[:-len(self.COMMENTSUFFIX)]
903  self.setComment(name, value)
904  return
905  if isinstance(value, Mapping):
906  # Create a property set instead
907  ps = PropertySet()
908  for k, v in value.items():
909  ps[k] = v
910  value = ps
911  self.set(name, value)
912 
913  def __reduce__(self):
914  # It would be a bit simpler to use __setstate__ and __getstate__.
915  # However, implementing __setstate__ in Python causes segfaults
916  # because pickle creates a new instance by calling
917  # object.__new__(PropertyList, *args) which bypasses
918  # the pybind11 memory allocation step.
919  return (_makePropertyList, (getPropertyListState(self),))
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.__setitem__
def __setitem__(self, name, value)
Definition: propertyContainerContinued.py:585
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.toDict
def toDict(self)
Definition: propertyContainerContinued.py:526
lsst::daf::base.propertyContainer.propertyContainerContinued.setPropertyListState
def setPropertyListState(container, state)
Definition: propertyContainerContinued.py:130
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.get
def get(self, name, default=None)
Definition: propertyContainerContinued.py:385
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList.set
def set(self, name, value, comment=None)
Definition: propertyContainerContinued.py:740
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList._typeMenu
dictionary _typeMenu
Definition: propertyContainerContinued.py:656
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.__copy__
def __copy__(self)
Definition: propertyContainerContinued.py:561
lsst::daf::base.propertyContainer.propertyContainerContinued.ReturnStyle
Definition: propertyContainerContinued.py:145
lsst::daf::base.propertyContainer.propertyContainerContinued.setPropertySetState
def setPropertySetState(container, state)
Definition: propertyContainerContinued.py:112
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.__iter__
def __iter__(self)
Definition: propertyContainerContinued.py:631
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.keys
def keys(self)
Definition: propertyContainerContinued.py:635
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet
Definition: propertyContainerContinued.py:373
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList.get
def get(self, name, default=None)
Definition: propertyContainerContinued.py:670
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.set
def set(self, name, value)
Definition: propertyContainerContinued.py:458
lsst.pex.config.history.format
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:174
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.__len__
def __len__(self)
Definition: propertyContainerContinued.py:628
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList.getScalar
def getScalar(self, name)
Definition: propertyContainerContinued.py:717
lsst::daf::base.propertyContainer.propertyContainerContinued.getPropertySetState
def getPropertySetState(container, asLists=False)
Definition: propertyContainerContinued.py:40
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet._typeMenu
dictionary _typeMenu
Definition: propertyContainerContinued.py:376
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList.__setitem__
def __setitem__(self, name, value)
Definition: propertyContainerContinued.py:883
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.getScalar
def getScalar(self, name)
Definition: propertyContainerContinued.py:435
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.__contains__
def __contains__(self, name)
Definition: propertyContainerContinued.py:573
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList.COMMENTSUFFIX
string COMMENTSUFFIX
Definition: propertyContainerContinued.py:666
id
table::Key< int > id
Definition: Detector.cc:162
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.getArray
def getArray(self, name)
Definition: propertyContainerContinued.py:412
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.__eq__
def __eq__(self, other)
Definition: propertyContainerContinued.py:545
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList.toOrderedDict
def toOrderedDict(self)
Definition: propertyContainerContinued.py:832
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList.__deepcopy__
def __deepcopy__(self, memo)
Definition: propertyContainerContinued.py:874
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList.toList
def toList(self)
Definition: propertyContainerContinued.py:810
lsst::utils
Definition: Backtrace.h:29
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.add
def add(self, name, value)
Definition: propertyContainerContinued.py:473
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.__reduce__
def __reduce__(self)
Definition: propertyContainerContinued.py:644
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList.__reduce__
def __reduce__(self)
Definition: propertyContainerContinued.py:913
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList.setComment
def setComment(self, name, comment)
Definition: propertyContainerContinued.py:791
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.__str__
def __str__(self)
Definition: propertyContainerContinued.py:625
type
table::Key< int > type
Definition: Detector.cc:163
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.items
def items(self)
Definition: propertyContainerContinued.py:638
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.__deepcopy__
def __deepcopy__(self, memo)
Definition: propertyContainerContinued.py:568
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList.__copy__
def __copy__(self)
Definition: propertyContainerContinued.py:867
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList.__iter__
def __iter__(self)
Definition: propertyContainerContinued.py:879
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList.__eq__
def __eq__(self, other)
Definition: propertyContainerContinued.py:854
lsst.pex::exceptions
Definition: Exception.h:37
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList
Definition: propertyContainerContinued.py:654
lsst::daf::base.propertyContainer.propertyContainerContinued.getPropertyListState
def getPropertyListState(container, asLists=False)
Definition: propertyContainerContinued.py:75
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.update
def update(self, addition)
Definition: propertyContainerContinued.py:502
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList.add
def add(self, name, value, comment=None)
Definition: propertyContainerContinued.py:758
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.values
def values(self)
Definition: propertyContainerContinued.py:641
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertyList.getArray
def getArray(self, name)
Definition: propertyContainerContinued.py:697
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.__delitem__
def __delitem__(self, name)
Definition: propertyContainerContinued.py:619
lsst::daf::base.propertyContainer.propertyContainerContinued.PropertySet.__getitem__
def __getitem__(self, name)
Definition: propertyContainerContinued.py:609