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