25__all__ = [
"getPropertySetState",
"getPropertyListState",
"setPropertySetState",
"setPropertyListState"]
37from .._dafBaseLib
import PropertySet, PropertyList
38from ..dateTime
import DateTime
42for checkType
in (
"Bool",
"Short",
"Int",
"Long",
"LongLong",
"UnsignedLongLong",
43 "Float",
"Double",
"String",
"DateTime",
44 "PropertySet",
"Undef"):
45 type_obj = getattr(PropertySet,
"TYPE_" + checkType)
46 _TYPE_MAP[type_obj] = checkType
48 _TYPE_MAP[checkType] = type_obj
51def getPropertySetState(container, asLists=False):
52 """Get the state of a PropertySet in a form that can be pickled.
56 container : `PropertySet`
57 The property container.
58 asLists : `bool`, optional
59 If False, the default, `tuple` will be used
for the contents. If true
60 a `list` will be used.
64 state : `list` of `tuple`
or `list` of `list`
65 The state,
as a list of tuples (
or lists), each of which contains
66 the following 3 items:
70 elementTypeName (a `str`)
71 the suffix of a ``setX`` method name
72 which
is appropriate
for the data type. For example integer
73 data has ``elementTypeName=
"Int"` which corresponds to
74 the ``setInt`` method.
76 the data
for the item,
in a form compatible
77 with the set method named by ``elementTypeName``
79 names = container.names(topLevelOnly=True)
80 sequence = list
if asLists
else tuple
86def getPropertyListState(container, asLists=False):
87 """Get the state of a PropertyList in a form that can be pickled.
91 container : `PropertyList`
92 The property container.
93 asLists : `bool`, optional
94 If False, the default, `tuple` will be used
for the contents. If true
95 a `list` will be used.
99 state : `list` of `tuple`
or `list` of `list`
100 The state,
as a list of tuples (
or lists), each of which contains
101 the following 4 items:
105 elementTypeName (a `str`):
106 the suffix of a ``setX`` method name
107 which
is appropriate
for the data type. For example integer
108 data has ``elementTypeName=
"Int"` which corresponds to
109 the ``setInt`` method.
111 the data
for the item,
in a form compatible
112 with the set method named by ``elementTypeName``
113 comment (a `str`): the comment. This item
is only present
114 if ``container``
is a PropertyList.
116 sequence = list if asLists
else tuple
119 container.getComment(name)))
120 for name
in container.getOrderedNames()]
123def setPropertySetState(container, state):
124 """Restore the state of a PropertySet, in place.
128 container : `PropertySet`
129 The property container whose state is to be restored.
130 It should be empty to start
with and is updated
in place.
132 The state,
as returned by `getPropertySetState`
134 for name, elemType, value
in state:
135 if elemType
is not None:
136 getattr(container,
"set" + elemType)(name, value)
138 raise ValueError(f
"Unrecognized values for state restoration: ({name}, {elemType}, {value})")
141def setPropertyListState(container, state):
142 """Restore the state of a PropertyList, in place.
146 container : `PropertyList`
147 The property container whose state is to be restored.
148 It should be empty to start
with and is updated
in place.
150 The state,
as returned by ``getPropertyListState``
152 for name, elemType, value, comment
in state:
153 getattr(container,
"set" + elemType)(name, value, comment)
163 """Return name of the type of a particular element
168 Container including the element
173 t = container.typeOf(name)
174 except LookupError
as e:
177 raise KeyError(str(e))
from None
179 return _TYPE_MAP.get(t,
None)
183 """Get a value of unknown type as a scalar or array
188 Container
from which to get the value
191 returnStyle : `ReturnStyle`
192 Control whether numeric
or string data
is returned
as an array
193 or scalar (the other types, ``PropertyList``, ``PropertySet``
194 and ``PersistablePtr``, are always returned
as a scalar):
195 - ReturnStyle.ARRAY:
return numeric
or string data types
196 as an array of values.
197 - ReturnStyle.SCALAR:
return numeric
or string data types
198 as a single value;
if the item has multiple values then
199 return the last value.
200 - ReturnStyle.AUTO: (deprecated)
return numeric
or string data
201 as a scalar
if there
is just one item,
or as an array
207 Raised
if the specified key does
not exist
in the container.
209 Raised
if the value retrieved
is of an unexpected type.
211 Raised
if the value
for ``returnStyle``
is not correct.
213 if not container.exists(name):
214 raise KeyError(name +
" not found")
215 if returnStyle
not in ReturnStyle:
216 raise ValueError(
"returnStyle {} must be a ReturnStyle".format(returnStyle))
219 if elemType
and elemType !=
"PropertySet":
220 value = getattr(container,
"getArray" + elemType)(name)
221 if returnStyle == ReturnStyle.ARRAY
or (returnStyle == ReturnStyle.AUTO
and len(value) > 1):
225 if container.isPropertySetPtr(name):
227 return container.getAsPropertyListPtr(name)
229 return container.getAsPropertySetPtr(name)
231 return container.getAsPersistablePtr(name)
234 raise TypeError(
'Unknown PropertySet value type for ' + name)
238 """Make input iterable.
240 Takes whatever is given to it
and yields it back one element at a time.
241 If it
is not an iterable
or it
is a string
or PropertySet/List,
244 if isinstance(a, (str, PropertyList, PropertySet)):
254 """Given an existing container and name, determine the type
255 that should be used for the supplied value. The supplied value
256 is assumed to be a scalar.
258 On Python 3 all ints are LongLong but we need to be able to store them
259 in Int containers
if that
is what
is being used (testing
for truncation).
260 Int
is assumed to mean 32bit integer (2147483647 to -2147483648).
262 If there
is no pre-existing value we have to decide what to do. For now
263 we pick Int
if the value
is less than maxsize.
268 Container
from which to get the value
274 Value to be assigned a type. Can be an iterable.
278 useType : `str`
or none
279 Type to use
for the supplied value. `
None`
if the input
is
280 `bool`
or a non-integral value.
284 maxLongLong = 2**63 - 1
295 if not isinstance(v, numbers.Integral)
or isinstance(v, bool):
307 if min
is None or max
is None:
308 raise RuntimeError(f
"Internal logic failure calculating integer range of {value}")
310 def _choose_int_from_range(int_value, current_type):
313 if current_type
not in {
"Int",
"LongLong",
"UnsignedLongLong"}:
316 if int_value <= maxInt
and int_value >= minInt
and current_type
in (
None,
"Int"):
320 elif int_value >= minLongLong
and int_value < 0:
323 use_type =
"LongLong"
324 elif int_value >= 0
and int_value <= maxLongLong
and current_type
in (
None,
"Int",
"LongLong"):
326 use_type =
"LongLong"
327 elif int_value <= maxU64
and int_value >= minU64:
328 use_type =
"UnsignedLongLong"
330 raise RuntimeError(
"Unable to guess integer type for storing out of "
331 f
"range value: {int_value}")
334 if container.exists(name):
339 useTypeMin = _choose_int_from_range(min, containerType)
340 useTypeMax = _choose_int_from_range(max, containerType)
342 if useTypeMin == useTypeMax:
350 choices = {useTypeMin, useTypeMax}
351 if choices == {
"Int",
"LongLong"}:
357 if "UnsignedLongLong" in choices:
358 return "UnsignedLongLong"
360 raise RuntimeError(f
"Logic error in guessing integer type from {min} and {max}")
364 """Set a single Python value of unknown type
368 except StopIteration:
375 if setType
is not None or t
in typeMenu:
377 setType = typeMenu[t]
378 return getattr(container,
"set" + setType)(name, value, *args)
380 for checkType
in typeMenu:
381 if (checkType
is None and exemplar
is None)
or \
382 (checkType
is not None and isinstance(exemplar, checkType)):
383 return getattr(container,
"set" + typeMenu[checkType])(name, value, *args)
384 raise TypeError(
"Unknown value type for key '%s': %s" % (name, t))
388 """Add a single Python value of unknown type
392 except StopIteration:
399 if addType
is not None or t
in typeMenu:
401 addType = typeMenu[t]
402 return getattr(container,
"add" + addType)(name, value, *args)
404 for checkType
in typeMenu:
405 if (checkType
is None and exemplar
is None)
or \
406 (checkType
is not None and isinstance(exemplar, checkType)):
407 return getattr(container,
"add" + typeMenu[checkType])(name, value, *args)
408 raise TypeError(
"Unknown value type for key '%s': %s" % (name, t))
412 """Make a `PropertySet` from the state returned by `getPropertySetState`
417 The data returned by `getPropertySetState`.
420 setPropertySetState(ps, state)
425 """Make a `PropertyList` from the state returned by
426 `getPropertyListState`
431 The data returned by `getPropertySetState`.
434 setPropertyListState(pl, state)
442 _typeMenu = {bool:
"Bool",
445 DateTime:
"DateTime",
446 PropertySet:
"PropertySet",
447 PropertyList:
"PropertySet",
453 """Create a `PropertySet` from a mapping or dict-like object.
457 metadata : `collections.abc.Mapping`
458 Metadata from which to create the `PropertySet`.
459 Can be a mapping, a `~dataclasses.dataclass`
or anything that
460 supports ``
toDict()``, ``to_dict()``
or ``dict()`` method.
461 It
is assumed that the dictionary
is expanded recursively by these
462 methods
or that the Python type can be understood by `PropertySet`.
467 The new `PropertySet`.
471 if isinstance(metadata, Mapping):
473 elif dataclasses.is_dataclass(metadata):
474 d = dataclasses.asdict(metadata)
476 for attr
in (
"to_dict",
"toDict",
"dict"):
477 if hasattr(metadata, attr):
478 d = getattr(metadata, attr)()
481 raise ValueError(
"Unable to extract mappings from the supplied metadata of type"
482 f
" {type(metadata)}")
486 def get(self, name, default=None):
487 """Return an item as a scalar, else default.
489 Identical to `getScalar` except that a default value
is returned
490 if the requested key
is not present. If an array item
is requested
491 the final value
in the array will be returned.
497 default : `object`, optional
498 Default value to use
if the named item
is not present.
502 value : any type supported by container
503 Single value of any type supported by the container,
else the
504 default value
if the requested item
is not present
in the
505 container. For array items the most recently added value
is
514 """Return an item as an array if the item is numeric or string
516 If the item is a `PropertySet`, `PropertyList`
or
517 `lsst.daf.base.PersistablePtr` then
return the item
as a scalar.
526 values : `list` of any type supported by container
527 The contents of the item, guaranteed to be returned
as a `list.`
532 Raised
if the item does
not exist.
537 """Return an item as a scalar
539 If the item has more than one value then the last value is returned.
549 Value stored
in the item. If the item refers to an array the
550 most recently added value
is returned.
555 Raised
if the item does
not exist.
559 def set(self, name, value):
560 """Set the value of an item
562 If the item already exists it is silently replaced; the types
569 value : any supported type
570 Value of item; may be a scalar
or array
574 def add(self, name, value):
575 """Append one or more values to a given item, which need not exist
577 If the item exists then the new value(s) are appended;
578 otherwise it is like calling `set`
584 value : any supported type
585 Value of item; may be a scalar
or array
591 the existing value. Also the item
is added
as a live
592 reference, so updating ``value`` will update this container
597 lsst::pex::exceptions::TypeError
598 Raised
if the type of `value`
is incompatible
with the existing
604 """Update the current container with the supplied additions.
608 addition : `collections.abc.Mapping` or `PropertySet`
609 The content to merge into the current container.
613 This
is not the same
as calling `PropertySet.combine` since the
614 behavior differs when both mappings contain the same key. This
615 method updates by overwriting existing values completely
with
618 if isinstance(addition, PropertySet):
622 self.copy(k, addition, k)
624 for k, v
in addition.items():
628 """Returns a (possibly nested) dictionary with all properties.
633 Dictionary with all names
and values (no comments).
637 for name
in self.names():
640 if isinstance(v, PropertySet):
641 d[name] = PropertySet.toDict(v)
648 return NotImplemented
650 if len(self) != len(other):
654 if (self_typeOf := self.typeOf(name)) != other.typeOf(name):
662 if self_typeOf
in (_TYPE_MAP[
"Float"], _TYPE_MAP[
"Double"]) \
663 and math.isnan(v1)
and math.isnan(v2):
673 for itemName
in self:
674 ps.copy(itemName, self, itemName)
678 result = self.deepCopy()
679 memo[
id(self)] = result
683 """Determines if the name is found at the top level hierarchy
688 Does not use `PropertySet.exists()`` because that includes support
689 for "."-delimited names. This method
is consistent
with the
690 items returned
from ``__iter__``.
692 return name
in self.names(topLevelOnly=
True)
695 """Assigns the supplied value to the container.
700 Name of item to update.
701 value : Value to assign
702 Can be any value supported by the container's ``set()``
703 method. `~collections.abc.Mapping` are converted to
704 `PropertySet` before assignment.
708 Uses `PropertySet.set`, overwriting any previous value.
710 if isinstance(value, Mapping):
713 for k, v
in value.items():
716 self.
set(name, value)
719 """Returns a scalar item from the container.
723 Uses `PropertySet.getScalar` to guarantee that a single value
729 if self.exists(name):
733 raise KeyError(f
"{name} not present in dict")
736 return self.toString()
739 return self.nameCount(topLevelOnly=
True)
742 for n
in self.names(topLevelOnly=
True):
746 return KeysView(self)
749 return ItemsView(self)
752 return ValuesView(self)
754 def pop(self, name, default=None):
755 """Remove the named key and return its value.
760 Name of the key to remove. Can be hierarchical.
761 default : Any, optional
762 Value to return if the key
is not present.
767 The value of the item
as would be returned using `
getScalar()`.
772 Raised
if no default
is given
and the key
is missing.
774 if self.exists(name):
789 return (_makePropertySet, (getPropertySetState(self),))
795 _typeMenu = {bool:
"Bool",
799 DateTime:
"DateTime",
800 PropertySet:
"PropertySet",
801 PropertyList:
"PropertySet",
805 COMMENTSUFFIX =
"#COMMENT"
806 """Special suffix used to indicate that a named item being assigned
807 using dict syntax is referring to a comment,
not value.
"""
809 def get(self, name, default=None):
810 """Return an item as a scalar, else default.
812 Identical to `getScalar` except that a default value
is returned
813 if the requested key
is not present. If an array item
is requested
814 the final value
in the array will be returned.
820 default : `object`, optional
821 Default value to use
if the named item
is not present.
825 value : any type supported by container
826 Single value of any type supported by the container,
else the
827 default value
if the requested item
is not present
in the
828 container. For array items the most recently added value
is
837 """Return an item as a list.
846 values : `list` of values
847 The contents of the item, guaranteed to be returned as a `list.`
852 Raised
if the item does
not exist.
857 """Return an item as a scalar
859 If the item has more than one value then the last value is returned.
869 Value stored
in the item. If the item refers to an array the
870 most recently added value
is returned.
875 Raised
if the item does
not exist.
879 def set(self, name, value, comment=None):
880 """Set the value of an item
882 If the item already exists it is silently replaced; the types
889 value : any supported type
890 Value of item; may be a scalar
or array
893 if comment
is not None:
897 def add(self, name, value, comment=None):
898 """Append one or more values to a given item, which need not exist
900 If the item exists then the new value(s) are appended;
901 otherwise it is like calling `set`
907 value : any supported type
908 Value of item; may be a scalar
or array
913 using dotted names (e.g.
if name=
"a" and value contains
914 an item
"b" which
is another PropertySet
and contains an
915 item
"c" which
is numeric
or string, then the value of
"c"
916 is added
as "a.b.c", appended to the existing values of
917 "a.b.c" if any (
in which case the types must be compatible).
921 lsst::pex::exceptions::TypeError
922 Raise
if the type of ``value``
is incompatible
with the existing
926 if comment
is not None:
931 """Set the comment for an existing entry.
936 Name of the key to receive updated comment.
943 if self.isArray(name):
947 getattr(self, f
"set{containerType}")(name, value, comment)
950 """Return a list of tuples of name, value, comment for each property
951 in the order that they were inserted.
955 ret : `list` of `tuple`
956 Tuples of name, value, comment
for each property
in the order
957 in which they were inserted.
959 orderedNames = self.getOrderedNames()
961 for name
in orderedNames:
962 if self.isArray(name):
965 ret.append((name, v, self.getComment(name)))
968 self.getComment(name)))
972 """Return an ordered dictionary with all properties in the order that
978 Ordered dictionary with all properties
in the order that they
979 were inserted. Comments are
not included.
983 As of Python 3.6 dicts retain their insertion order.
991 toDict = toOrderedDict
997 if not PropertySet.__eq__(self, other):
1001 if self.getComment(name) != other.getComment(name):
1009 for itemName
in self:
1010 pl.copy(itemName, self, itemName)
1014 result = self.deepCopy()
1015 memo[
id(self)] = result
1019 for n
in self.getOrderedNames():
1023 """Assigns the supplied value to the container.
1028 Name of item to update. If the name ends with
1029 `PropertyList.COMMENTSUFFIX`, the comment
is updated rather
1031 value : Value to assign
1032 Can be any value supported by the container
's ``set()``
1033 method. `~collections.abc.Mapping` are converted to
1034 `PropertySet` before assignment.
1038 Uses `PropertySet.set`, overwriting any previous value.
1044 if isinstance(value, Mapping):
1047 for k, v
in value.items():
1050 self.
set(name, value)
1058 return (_makePropertyList, (getPropertyListState(self),))
std::vector< SchemaItem< Flag > > * items
Class for storing ordered metadata with comments.
Class for storing generic metadata.
set(self, name, value, comment=None)
setComment(self, name, comment)
add(self, name, value, comment=None)
get(self, name, default=None)
__setitem__(self, name, value)
pop(self, name, default=None)
from_mapping(cls, metadata)
__setitem__(self, name, value)
get(self, name, default=None)
daf::base::PropertySet * set
_propertyContainerAdd(container, name, value, typeMenu, *args)
_guessIntegerType(container, name, value)
_propertyContainerSet(container, name, value, typeMenu, *args)
_propertyContainerGet(container, name, returnStyle)
_propertyContainerElementTypeName(container, name)