LSSTApplications  18.1.0
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
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  If the supplied parameter is a `PropertySet` then the
480  `PropertySet.combine` method will be used. If the supplied parameter
481  is a `collections.abc.Mapping` each item will be copied out of the
482  mapping and value assigned using `PropertySet.set`, overwriting
483  any previous values.
484  """
485  if isinstance(addition, PropertySet):
486  self.combine(addition)
487  else:
488  for k, v in addition.items():
489  self[k] = v
490 
491  def toDict(self):
492  """Returns a (possibly nested) dictionary with all properties.
493 
494  Returns
495  -------
496  d : `dict`
497  Dictionary with all names and values (no comments).
498  """
499 
500  d = {}
501  for name in self.names():
502  v = _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO)
503 
504  if isinstance(v, PropertySet):
505  d[name] = PropertySet.toDict(v)
506  else:
507  d[name] = v
508  return d
509 
510  def __eq__(self, other):
511  if type(self) != type(other):
512  return False
513 
514  if len(self) != len(other):
515  return False
516 
517  for name in self:
518  if _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO) != \
519  _propertyContainerGet(other, name, returnStyle=ReturnStyle.AUTO):
520  return False
521  if self.typeOf(name) != other.typeOf(name):
522  return False
523 
524  return True
525 
526  def __copy__(self):
527  # Copy without having to go through pickle state
528  ps = PropertySet()
529  for itemName in self:
530  ps.copy(itemName, self, itemName)
531  return ps
532 
533  def __deepcopy__(self, memo):
534  result = self.deepCopy()
535  memo[id(self)] = result
536  return result
537 
538  def __contains__(self, name):
539  """Determines if the name is found at the top level hierarchy
540  of the container.
541 
542  Notes
543  ------
544  Does not use `PropertySet.exists()`` because that includes support
545  for "."-delimited names. This method is consistent with the
546  items returned from ``__iter__``.
547  """
548  return name in self.names(topLevelOnly=True)
549 
550  def __setitem__(self, name, value):
551  """Assigns the supplied value to the container.
552 
553  Parameters
554  ----------
555  name : `str`
556  Name of item to update.
557  value : Value to assign
558  Can be any value supported by the container's ``set()``
559  method. `~collections.abc.Mapping` are converted to
560  `PropertySet` before assignment.
561 
562  Notes
563  -----
564  Uses `PropertySet.set`, overwriting any previous value.
565  """
566  if isinstance(value, Mapping):
567  # Create a property set instead
568  ps = PropertySet()
569  for k, v in value.items():
570  ps[k] = v
571  value = ps
572  self.set(name, value)
573 
574  def __getitem__(self, name):
575  """Returns a scalar item from the container.
576 
577  Notes
578  -----
579  Uses `PropertySet.getScalar` to guarantee that a single value
580  will be returned.
581  """
582  return self.getScalar(name)
583 
584  def __delitem__(self, name):
585  if name in self:
586  self.remove(name)
587  else:
588  raise KeyError(f"{name} not present in dict")
589 
590  def __str__(self):
591  return self.toString()
592 
593  def __len__(self):
594  return self.nameCount(topLevelOnly=True)
595 
596  def __iter__(self):
597  for n in self.names(topLevelOnly=True):
598  yield n
599 
600  def keys(self):
601  return KeysView(self)
602 
603  def __reduce__(self):
604  # It would be a bit simpler to use __setstate__ and __getstate__.
605  # However, implementing __setstate__ in Python causes segfaults
606  # because pickle creates a new instance by calling
607  # object.__new__(PropertyList, *args) which bypasses
608  # the pybind11 memory allocation step.
609  return (_makePropertySet, (getPropertySetState(self),))
610 
611 
612 @continueClass
614  # Mapping of type to method names
615  _typeMenu = {bool: "Bool",
616  int: "Int",
617  float: "Double",
618  str: "String",
619  DateTime: "DateTime",
620  PropertySet: "PropertySet",
621  PropertyList: "PropertySet",
622  None: "Undef",
623  }
624 
625  COMMENTSUFFIX = "#COMMENT"
626  """Special suffix used to indicate that a named item being assigned
627  using dict syntax is referring to a comment, not value."""
628 
629  def get(self, name, default=None):
630  """Return an item as a scalar, else default.
631 
632  Identical to `getScalar` except that a default value is returned
633  if the requested key is not present. If an array item is requested
634  the final value in the array will be returned.
635 
636  Parameters
637  ----------
638  name : ``str``
639  Name of item
640  default : `object`, optional
641  Default value to use if the named item is not present.
642 
643  Returns
644  -------
645  value : any type supported by container
646  Single value of any type supported by the container, else the
647  default value if the requested item is not present in the
648  container. For array items the most recently added value is
649  returned.
650  """
651  try:
652  return _propertyContainerGet(self, name, returnStyle=ReturnStyle.SCALAR)
653  except KeyError:
654  return default
655 
656  def getArray(self, name):
657  """Return an item as a list.
658 
659  Parameters
660  ----------
661  name : `str`
662  Name of item
663 
664  Returns
665  -------
666  values : `list` of values
667  The contents of the item, guaranteed to be returned as a `list.`
668 
669  Raises
670  ------
671  KeyError
672  Raised if the item does not exist.
673  """
674  return _propertyContainerGet(self, name, returnStyle=ReturnStyle.ARRAY)
675 
676  def getScalar(self, name):
677  """Return an item as a scalar
678 
679  If the item has more than one value then the last value is returned.
680 
681  Parameters
682  ----------
683  name : `str`
684  Name of item.
685 
686  Returns
687  -------
688  value : scalar item
689  Value stored in the item. If the item refers to an array the
690  most recently added value is returned.
691 
692  Raises
693  ------
694  KeyError
695  Raised if the item does not exist.
696  """
697  return _propertyContainerGet(self, name, returnStyle=ReturnStyle.SCALAR)
698 
699  def set(self, name, value, comment=None):
700  """Set the value of an item
701 
702  If the item already exists it is silently replaced; the types
703  need not match.
704 
705  Parameters
706  ----------
707  name : `str`
708  Name of item
709  value : any supported type
710  Value of item; may be a scalar or array
711  """
712  args = []
713  if comment is not None:
714  args.append(comment)
715  return _propertyContainerSet(self, name, value, self._typeMenu, *args)
716 
717  def add(self, name, value, comment=None):
718  """Append one or more values to a given item, which need not exist
719 
720  If the item exists then the new value(s) are appended;
721  otherwise it is like calling `set`
722 
723  Parameters
724  ----------
725  name : `str`
726  Name of item
727  value : any supported type
728  Value of item; may be a scalar or array
729 
730  Notes
731  -----
732  If `value` is an `lsst.daf.base.PropertySet` items are added
733  using dotted names (e.g. if name="a" and value contains
734  an item "b" which is another PropertySet and contains an
735  item "c" which is numeric or string, then the value of "c"
736  is added as "a.b.c", appended to the existing values of
737  "a.b.c" if any (in which case the types must be compatible).
738 
739  Raises
740  ------
741  lsst::pex::exceptions::TypeError
742  Raise if the type of ``value`` is incompatible with the existing
743  value of the item.
744  """
745  args = []
746  if comment is not None:
747  args.append(comment)
748  return _propertyContainerAdd(self, name, value, self._typeMenu, *args)
749 
750  def setComment(self, name, comment):
751  """Set the comment for an existing entry.
752 
753  Parameters
754  ----------
755  name : `str`
756  Name of the key to receive updated comment.
757  comment : `comment`
758  New comment string.
759  """
760  # The only way to do this is to replace the existing entry with
761  # one that has the new comment
762  containerType = _propertyContainerElementTypeName(self, name)
763  if self.isArray(name):
764  value = self.getArray(name)
765  else:
766  value = self.getScalar(name)
767  getattr(self, f"set{containerType}")(name, value, comment)
768 
769  def toList(self):
770  """Return a list of tuples of name, value, comment for each property
771  in the order that they were inserted.
772 
773  Returns
774  -------
775  ret : `list` of `tuple`
776  Tuples of name, value, comment for each property in the order
777  in which they were inserted.
778  """
779  orderedNames = self.getOrderedNames()
780  ret = []
781  for name in orderedNames:
782  if self.isArray(name):
783  values = _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO)
784  for v in values:
785  ret.append((name, v, self.getComment(name)))
786  else:
787  ret.append((name, _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO),
788  self.getComment(name)))
789  return ret
790 
791  def toOrderedDict(self):
792  """Return an ordered dictionary with all properties in the order that
793  they were inserted.
794 
795  Returns
796  -------
797  d : `dict`
798  Ordered dictionary with all properties in the order that they
799  were inserted. Comments are not included.
800 
801  Notes
802  -----
803  As of Python 3.6 dicts retain their insertion order.
804  """
805  d = {}
806  for name in self:
807  d[name] = _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO)
808  return d
809 
810  # For PropertyList the two are equivalent
811  toDict = toOrderedDict
812 
813  def __eq__(self, other):
814  # super() doesn't seem to work properly in @continueClass;
815  # note that super with arguments seems to work at first, but actually
816  # doesn't either.
817  if not PropertySet.__eq__(self, other):
818  return False
819 
820  for name in self:
821  if self.getComment(name) != other.getComment(name):
822  return False
823 
824  return True
825 
826  def __copy__(self):
827  # Copy without having to go through pickle state
828  pl = PropertyList()
829  for itemName in self:
830  pl.copy(itemName, self, itemName)
831  return pl
832 
833  def __deepcopy__(self, memo):
834  result = self.deepCopy()
835  memo[id(self)] = result
836  return result
837 
838  def __iter__(self):
839  for n in self.getOrderedNames():
840  yield n
841 
842  def __setitem__(self, name, value):
843  """Assigns the supplied value to the container.
844 
845  Parameters
846  ----------
847  name : `str`
848  Name of item to update. If the name ends with
849  `PropertyList.COMMENTSUFFIX`, the comment is updated rather
850  than the value.
851  value : Value to assign
852  Can be any value supported by the container's ``set()``
853  method. `~collections.abc.Mapping` are converted to
854  `PropertySet` before assignment.
855 
856  Notes
857  -----
858  Uses `PropertySet.set`, overwriting any previous value.
859  """
860  if name.endswith(self.COMMENTSUFFIX):
861  name = name[:-len(self.COMMENTSUFFIX)]
862  self.setComment(name, value)
863  return
864  if isinstance(value, Mapping):
865  # Create a property set instead
866  ps = PropertySet()
867  for k, v in value.items():
868  ps[k] = v
869  value = ps
870  self.set(name, value)
871 
872  def __reduce__(self):
873  # It would be a bit simpler to use __setstate__ and __getstate__.
874  # However, implementing __setstate__ in Python causes segfaults
875  # because pickle creates a new instance by calling
876  # object.__new__(PropertyList, *args) which bypasses
877  # the pybind11 memory allocation step.
878  return (_makePropertyList, (getPropertyListState(self),))
table::Key< int > id
Definition: Detector.cc:166
table::Key< int > type
Definition: Detector.cc:167
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:168