25__all__ = [
"getPropertySetState",
"getPropertyListState",
"setPropertySetState",
"setPropertyListState"]
29from collections.abc
import Mapping, KeysView, ValuesView, ItemsView
33from lsst.utils
import continueClass
35from .propertySet
import PropertySet
36from .propertyList
import PropertyList
37from ..dateTime
import DateTime
41 """Get the state of a PropertySet in a form that can be pickled.
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.
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:
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.
65 the data
for the item,
in a form compatible
66 with the set method named by ``elementTypeName``
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)))
76 """Get the state of a PropertyList in a form that can be pickled.
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.
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:
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.
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.
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()]
113 """Restore the state of a PropertySet, in place.
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.
121 The state,
as returned by `getPropertySetState`
123 for name, elemType, value
in state:
124 if elemType
is not None:
125 getattr(container,
"set" + elemType)(name, value)
127 raise ValueError(f
"Unrecognized values for state restoration: ({name}, {elemType}, {value})")
131 """Restore the state of a PropertyList, in place.
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.
139 The state,
as returned by ``getPropertyListState``
141 for name, elemType, value, comment
in state:
142 getattr(container,
"set" + elemType)(name, value, comment)
151def _propertyContainerElementTypeName(container, name):
152 """Return name of the type of a particular element
157 Container including the element
162 t = container.typeOf(name)
163 except LookupError
as e:
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):
175def _propertyContainerGet(container, name, returnStyle):
176 """Get a value of unknown type as a scalar or array
181 Container
from which to get the value
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
200 Raised
if the specified key does
not exist
in the container.
202 Raised
if the value retrieved
is of an unexpected type.
204 Raised
if the value
for ``returnStyle``
is not correct.
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))
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):
218 if container.isPropertySetPtr(name):
220 return container.getAsPropertyListPtr(name)
222 return container.getAsPropertySetPtr(name)
224 return container.getAsPersistablePtr(name)
227 raise TypeError(
'Unknown PropertySet value type for ' + name)
231 """Make input iterable.
233 Takes whatever is given to it
and yields it back one element at a time.
234 If it
is not an iterable
or it
is a string
or PropertySet/List,
237 if isinstance(a, (str, PropertyList, PropertySet)):
246def _guessIntegerType(container, name, value):
247 """Given an existing container and name, determine the type
248 that should be used for the supplied value. The supplied value
249 is assumed to be a scalar.
251 On Python 3 all ints are LongLong but we need to be able to store them
252 in Int containers
if that
is what
is being used (testing
for truncation).
253 Int
is assumed to mean 32bit integer (2147483647 to -2147483648).
255 If there
is no pre-existing value we have to decide what to do. For now
256 we pick Int
if the value
is less than maxsize.
261 Container
from which to get the value
267 Value to be assigned a type. Can be an iterable.
271 useType : `str`
or none
272 Type to use
for the supplied value. `
None`
if the input
is
273 `bool`
or a non-integral value.
277 maxLongLong = 2**63 - 1
286 for v
in _iterable(value):
288 if not isinstance(v, numbers.Integral)
or isinstance(v, bool):
300 if min
is None or max
is None:
301 raise RuntimeError(f
"Internal logic failure calculating integer range of {value}")
303 def _choose_int_from_range(int_value, current_type):
306 if current_type
not in {
"Int",
"LongLong",
"UnsignedLongLong"}:
309 if int_value <= maxInt
and int_value >= minInt
and current_type
in (
None,
"Int"):
313 elif int_value >= minLongLong
and int_value < 0:
316 use_type =
"LongLong"
317 elif int_value >= 0
and int_value <= maxLongLong
and current_type
in (
None,
"Int",
"LongLong"):
319 use_type =
"LongLong"
320 elif int_value <= maxU64
and int_value >= minU64:
321 use_type =
"UnsignedLongLong"
323 raise RuntimeError(
"Unable to guess integer type for storing out of "
324 f
"range value: {int_value}")
328 containerType = _propertyContainerElementTypeName(container, name)
332 useTypeMin = _choose_int_from_range(min, containerType)
333 useTypeMax = _choose_int_from_range(max, containerType)
335 if useTypeMin == useTypeMax:
343 choices = {useTypeMin, useTypeMax}
344 if choices == {
"Int",
"LongLong"}:
350 if "UnsignedLongLong" in choices:
351 return "UnsignedLongLong"
353 raise RuntimeError(f
"Logic error in guessing integer type from {min} and {max}")
356def _propertyContainerSet(container, name, value, typeMenu, *args):
357 """Set a single Python value of unknown type
360 exemplar = next(_iterable(value))
361 except StopIteration:
366 setType = _guessIntegerType(container, name, value)
368 if setType
is not None or t
in typeMenu:
370 setType = typeMenu[t]
371 return getattr(container,
"set" + setType)(name, value, *args)
373 for checkType
in typeMenu:
374 if (checkType
is None and exemplar
is None)
or \
375 (checkType
is not None and isinstance(exemplar, checkType)):
376 return getattr(container,
"set" + typeMenu[checkType])(name, value, *args)
377 raise TypeError(
"Unknown value type for key '%s': %s" % (name, t))
380def _propertyContainerAdd(container, name, value, typeMenu, *args):
381 """Add a single Python value of unknown type
384 exemplar = next(_iterable(value))
385 except StopIteration:
390 addType = _guessIntegerType(container, name, exemplar)
392 if addType
is not None or t
in typeMenu:
394 addType = typeMenu[t]
395 return getattr(container,
"add" + addType)(name, value, *args)
397 for checkType
in typeMenu:
398 if (checkType
is None and exemplar
is None)
or \
399 (checkType
is not None and isinstance(exemplar, checkType)):
400 return getattr(container,
"add" + typeMenu[checkType])(name, value, *args)
401 raise TypeError(
"Unknown value type for key '%s': %s" % (name, t))
404def _makePropertySet(state):
405 """Make a `PropertySet` from the state returned by `getPropertySetState`
410 The data returned by `getPropertySetState`.
417def _makePropertyList(state):
418 """Make a `PropertyList` from the state returned by
419 `getPropertyListState`
424 The data returned by `getPropertySetState`.
435 _typeMenu = {bool:
"Bool",
438 DateTime:
"DateTime",
439 PropertySet:
"PropertySet",
440 PropertyList:
"PropertySet",
444 def get(self, name, default=None):
445 """Return an item as a scalar, else default.
447 Identical to `getScalar` except that a default value
is returned
448 if the requested key
is not present. If an array item
is requested
449 the final value
in the array will be returned.
455 default : `object`, optional
456 Default value to use
if the named item
is not present.
460 value : any type supported by container
461 Single value of any type supported by the container,
else the
462 default value
if the requested item
is not present
in the
463 container. For array items the most recently added value
is
467 return _propertyContainerGet(self, name, returnStyle=ReturnStyle.SCALAR)
472 """Return an item as an array if the item is numeric or string
474 If the item is a `PropertySet`, `PropertyList`
or
475 `lsst.daf.base.PersistablePtr` then
return the item
as a scalar.
484 values : `list` of any type supported by container
485 The contents of the item, guaranteed to be returned
as a `list.`
490 Raised
if the item does
not exist.
492 return _propertyContainerGet(self, name, returnStyle=ReturnStyle.ARRAY)
495 """Return an item as a scalar
497 If the item has more than one value then the last value is returned.
507 Value stored
in the item. If the item refers to an array the
508 most recently added value
is returned.
513 Raised
if the item does
not exist.
515 return _propertyContainerGet(self, name, returnStyle=ReturnStyle.SCALAR)
517 def set(self, name, value):
518 """Set the value of an item
520 If the item already exists it is silently replaced; the types
527 value : any supported type
528 Value of item; may be a scalar
or array
530 return _propertyContainerSet(self, name, value, self.
_typeMenu_typeMenu)
532 def add(self, name, value):
533 """Append one or more values to a given item, which need not exist
535 If the item exists then the new value(s) are appended;
536 otherwise it is like calling `set`
542 value : any supported type
543 Value of item; may be a scalar
or array
549 the existing value. Also the item
is added
as a live
550 reference, so updating ``value`` will update this container
555 lsst::pex::exceptions::TypeError
556 Raised
if the type of `value`
is incompatible
with the existing
559 return _propertyContainerAdd(self, name, value, self.
_typeMenu_typeMenu)
562 """Update the current container with the supplied additions.
566 addition : `collections.abc.Mapping` or `PropertySet`
567 The content to merge into the current container.
571 This
is not the same
as calling `PropertySet.combine` since the
572 behavior differs when both mappings contain the same key. This
573 method updates by overwriting existing values completely
with
576 if isinstance(addition, PropertySet):
580 self.copy(k, addition, k)
582 for k, v
in addition.items():
586 """Returns a (possibly nested) dictionary with all properties.
591 Dictionary with all names
and values (no comments).
595 for name
in self.names():
596 v = _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO)
598 if isinstance(v, PropertySet):
599 d[name] = PropertySet.toDict(v)
605 if type(self) != type(other):
606 return NotImplemented
608 if len(self) != len(other):
612 if _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO) != \
613 _propertyContainerGet(other, name, returnStyle=ReturnStyle.AUTO):
615 if self.typeOf(name) != other.typeOf(name):
623 for itemName
in self:
624 ps.copy(itemName, self, itemName)
628 result = self.deepCopy()
629 memo[id(self)] = result
633 """Determines if the name is found at the top level hierarchy
638 Does not use `PropertySet.exists()`` because that includes support
639 for "."-delimited names. This method
is consistent
with the
640 items returned
from ``__iter__``.
642 return name
in self.names(topLevelOnly=
True)
645 """Assigns the supplied value to the container.
650 Name of item to update.
651 value : Value to assign
652 Can be any value supported by the container's ``set()``
653 method. `~collections.abc.Mapping` are converted to
654 `PropertySet` before assignment.
658 Uses `PropertySet.set`, overwriting any previous value.
660 if isinstance(value, Mapping):
663 for k, v
in value.items():
666 self.
setset(name, value)
669 """Returns a scalar item from the container.
673 Uses `PropertySet.getScalar` to guarantee that a single value
679 if self.exists(name):
683 raise KeyError(f
"{name} not present in dict")
686 return self.toString()
689 return self.nameCount(topLevelOnly=
True)
692 for n
in self.names(topLevelOnly=
True):
696 return KeysView(self)
699 return ItemsView(self)
702 return ValuesView(self)
704 def pop(self, name, default=None):
705 """Remove the named key and return its value.
710 Name of the key to remove. Can be hierarchical.
711 default : Any, optional
712 Value to return if the key
is not present.
717 The value of the item
as would be returned using `
getScalar()`.
722 Raised
if no default
is given
and the key
is missing.
724 if self.exists(name):
745 _typeMenu = {bool:
"Bool",
749 DateTime:
"DateTime",
750 PropertySet:
"PropertySet",
751 PropertyList:
"PropertySet",
755 COMMENTSUFFIX =
"#COMMENT"
756 """Special suffix used to indicate that a named item being assigned
757 using dict syntax is referring to a comment,
not value.
"""
759 def get(self, name, default=None):
760 """Return an item as a scalar, else default.
762 Identical to `getScalar` except that a default value
is returned
763 if the requested key
is not present. If an array item
is requested
764 the final value
in the array will be returned.
770 default : `object`, optional
771 Default value to use
if the named item
is not present.
775 value : any type supported by container
776 Single value of any type supported by the container,
else the
777 default value
if the requested item
is not present
in the
778 container. For array items the most recently added value
is
782 return _propertyContainerGet(self, name, returnStyle=ReturnStyle.SCALAR)
787 """Return an item as a list.
796 values : `list` of values
797 The contents of the item, guaranteed to be returned as a `list.`
802 Raised
if the item does
not exist.
804 return _propertyContainerGet(self, name, returnStyle=ReturnStyle.ARRAY)
807 """Return an item as a scalar
809 If the item has more than one value then the last value is returned.
819 Value stored
in the item. If the item refers to an array the
820 most recently added value
is returned.
825 Raised
if the item does
not exist.
827 return _propertyContainerGet(self, name, returnStyle=ReturnStyle.SCALAR)
829 def set(self, name, value, comment=None):
830 """Set the value of an item
832 If the item already exists it is silently replaced; the types
839 value : any supported type
840 Value of item; may be a scalar
or array
843 if comment
is not None:
845 return _propertyContainerSet(self, name, value, self.
_typeMenu_typeMenu, *args)
847 def add(self, name, value, comment=None):
848 """Append one or more values to a given item, which need not exist
850 If the item exists then the new value(s) are appended;
851 otherwise it is like calling `set`
857 value : any supported type
858 Value of item; may be a scalar
or array
863 using dotted names (e.g.
if name=
"a" and value contains
864 an item
"b" which
is another PropertySet
and contains an
865 item
"c" which
is numeric
or string, then the value of
"c"
866 is added
as "a.b.c", appended to the existing values of
867 "a.b.c" if any (
in which case the types must be compatible).
871 lsst::pex::exceptions::TypeError
872 Raise
if the type of ``value``
is incompatible
with the existing
876 if comment
is not None:
878 return _propertyContainerAdd(self, name, value, self.
_typeMenu_typeMenu, *args)
881 """Set the comment for an existing entry.
886 Name of the key to receive updated comment.
892 containerType = _propertyContainerElementTypeName(self, name)
893 if self.isArray(name):
897 getattr(self, f
"set{containerType}")(name, value, comment)
900 """Return a list of tuples of name, value, comment for each property
901 in the order that they were inserted.
905 ret : `list` of `tuple`
906 Tuples of name, value, comment
for each property
in the order
907 in which they were inserted.
909 orderedNames = self.getOrderedNames()
911 for name
in orderedNames:
912 if self.isArray(name):
913 values = _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO)
915 ret.append((name, v, self.getComment(name)))
917 ret.append((name, _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO),
918 self.getComment(name)))
922 """Return an ordered dictionary with all properties in the order that
928 Ordered dictionary with all properties
in the order that they
929 were inserted. Comments are
not included.
933 As of Python 3.6 dicts retain their insertion order.
937 d[name] = _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO)
941 toDict = toOrderedDict
947 if not PropertySet.__eq__(self, other):
951 if self.getComment(name) != other.getComment(name):
959 for itemName
in self:
960 pl.copy(itemName, self, itemName)
964 result = self.deepCopy()
965 memo[id(self)] = result
969 for n
in self.getOrderedNames():
973 """Assigns the supplied value to the container.
978 Name of item to update. If the name ends with
979 `PropertyList.COMMENTSUFFIX`, the comment
is updated rather
981 value : Value to assign
982 Can be any value supported by the container
's ``set()``
983 method. `~collections.abc.Mapping` are converted to
984 `PropertySet` before assignment.
988 Uses `PropertySet.set`, overwriting any previous value.
994 if isinstance(value, Mapping):
997 for k, v
in value.items():
1000 self.
setset(name, value)
Class for storing ordered metadata with comments.
Class for storing generic metadata.
def __deepcopy__(self, memo)
def set(self, name, value, comment=None)
def setComment(self, name, comment)
def get(self, name, default=None)
def __setitem__(self, name, value)
def add(self, name, value, comment=None)
def getScalar(self, name)
def __deepcopy__(self, memo)
def add(self, name, value)
def __contains__(self, name)
def __setitem__(self, name, value)
def get(self, name, default=None)
def __getitem__(self, name)
def pop(self, name, default=None)
def getScalar(self, name)
def __delitem__(self, name)
def update(self, addition)
def set(self, name, value)
def setPropertyListState(container, state)
def setPropertySetState(container, state)
def getPropertyListState(container, asLists=False)
def getPropertySetState(container, asLists=False)