25 __all__ = [
"getPropertySetState", 
"getPropertyListState", 
"setPropertySetState", 
"setPropertyListState"]
 
   29 from collections.abc 
import Mapping, KeysView, ValuesView, ItemsView
 
   35 from .propertySet 
import PropertySet
 
   36 from .propertyList 
import PropertyList
 
   37 from ..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: 
   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`` 
   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)))
 
   72     """Get the state of a PropertyList in a form that can be pickled. 
   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. 
   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. 
   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()]
 
  105     """Restore the state of a PropertySet, in place. 
  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. 
  113         The state, as returned by `getPropertySetState` 
  115     for name, elemType, value 
in state:
 
  116         if elemType 
is not None:
 
  117             getattr(container, 
"set" + elemType)(name, value)
 
  119             raise ValueError(f
"Unrecognized values for state restoration: ({name}, {elemType}, {value})")
 
  123     """Restore the state of a PropertyList, in place. 
  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. 
  131         The state, as returned by ``getPropertyListState`` 
  133     for name, elemType, value, comment 
in state:
 
  134         getattr(container, 
"set" + elemType)(name, value, comment)
 
  143 def _propertyContainerElementTypeName(container, name):
 
  144     """Return name of the type of a particular element""" 
  146         t = container.typeOf(name)
 
  147     except LookupError 
as e:
 
  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):
 
  159 def _propertyContainerGet(container, name, returnStyle):
 
  160     """Get a value of unknown type as a scalar or array 
  164     container : `lsst.daf.base.PropertySet` or `lsst.daf.base.PropertyList` 
  165         Container from which to get the value 
  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 
  184         Raised if the specified key does not exist in the container. 
  186         Raised if the value retrieved is of an unexpected type. 
  188         Raised if the value for ``returnStyle`` is not correct. 
  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))
 
  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):
 
  202     if container.isPropertySetPtr(name):
 
  204             return container.getAsPropertyListPtr(name)
 
  206             return container.getAsPropertySetPtr(name)
 
  208         return container.getAsPersistablePtr(name)
 
  211     raise TypeError(
'Unknown PropertySet value type for ' + name)
 
  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. 
  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). 
  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. 
  226     Returns None if the value supplied is a bool or not an integral value. 
  231     maxLongLong = 2**63 - 1
 
  238     if isinstance(value, bool):
 
  241     if isinstance(value, numbers.Integral):
 
  243             containerType = _propertyContainerElementTypeName(container, name)
 
  246             if value <= maxInt 
and value >= minInt:
 
  248             elif value <= maxLongLong 
and value >= minLongLong:
 
  250             elif value <= maxU64 
and value >= minU64:
 
  251                 useType = 
"UnsignedLongLong" 
  253                 raise RuntimeError(
"Unable to guess integer type for storing value: %d" % (value,))
 
  255             if containerType == 
"Int":
 
  261             elif containerType == 
"LongLong":
 
  263             elif containerType == 
"UnsignedLongLong":
 
  264                 useType = 
"UnsignedLongLong" 
  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)):
 
  276     setType = _guessIntegerType(container, name, exemplar)
 
  278     if setType 
is not None or t 
in typeMenu:
 
  280             setType = typeMenu[t]
 
  281         return getattr(container, 
"set" + setType)(name, value, *args)
 
  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))
 
  290 def _propertyContainerAdd(container, name, value, typeMenu, *args):
 
  291     """Add a single Python value of unknown type""" 
  292     if hasattr(value, 
"__iter__"):
 
  298     addType = _guessIntegerType(container, name, exemplar)
 
  300     if addType 
is not None or t 
in typeMenu:
 
  302             addType = typeMenu[t]
 
  303         return getattr(container, 
"add" + addType)(name, value, *args)
 
  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))
 
  312 def _makePropertySet(state):
 
  313     """Make a `PropertySet` from the state returned by `getPropertySetState` 
  318         The data returned by `getPropertySetState`. 
  325 def _makePropertyList(state):
 
  326     """Make a `PropertyList` from the state returned by 
  327     `getPropertyListState` 
  332         The data returned by `getPropertySetState`. 
  343     _typeMenu = {bool: 
"Bool",
 
  346                  DateTime: 
"DateTime",
 
  347                  PropertySet: 
"PropertySet",
 
  348                  PropertyList: 
"PropertySet",
 
  352     def get(self, name, default=None):
 
  353         """Return an item as a scalar, else default. 
  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. 
  363         default : `object`, optional 
  364             Default value to use if the named item is not present. 
  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 
  375             return _propertyContainerGet(self, name, returnStyle=ReturnStyle.SCALAR)
 
  380         """Return an item as an array if the item is numeric or string 
  382         If the item is a `PropertySet`, `PropertyList` or 
  383         `lsst.daf.base.PersistablePtr` then return the item as a scalar. 
  392         values : `list` of any type supported by container 
  393             The contents of the item, guaranteed to be returned as a `list.` 
  398             Raised if the item does not exist. 
  400         return _propertyContainerGet(self, name, returnStyle=ReturnStyle.ARRAY)
 
  403         """Return an item as a scalar 
  405         If the item has more than one value then the last value is returned. 
  415             Value stored in the item.  If the item refers to an array the 
  416             most recently added value is returned. 
  421             Raised if the item does not exist. 
  423         return _propertyContainerGet(self, name, returnStyle=ReturnStyle.SCALAR)
 
  425     def set(self, name, value):
 
  426         """Set the value of an item 
  428         If the item already exists it is silently replaced; the types 
  435         value : any supported type 
  436             Value of item; may be a scalar or array 
  438         return _propertyContainerSet(self, name, value, self.
_typeMenu)
 
  440     def add(self, name, value):
 
  441         """Append one or more values to a given item, which need not exist 
  443         If the item exists then the new value(s) are appended; 
  444         otherwise it is like calling `set` 
  450         value : any supported type 
  451             Value of item; may be a scalar or array 
  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 
  463         lsst::pex::exceptions::TypeError 
  464             Raised if the type of `value` is incompatible with the existing 
  467         return _propertyContainerAdd(self, name, value, self.
_typeMenu)
 
  470         """Update the current container with the supplied additions. 
  474         addition : `collections.abc.Mapping` or `PropertySet` 
  475             The content to merge into the current container. 
  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 
  484         if isinstance(addition, PropertySet):
 
  488                 self.copy(k, addition, k)
 
  490             for k, v 
in addition.items():
 
  494         """Returns a (possibly nested) dictionary with all properties. 
  499             Dictionary with all names and values (no comments). 
  503         for name 
in self.names():
 
  504             v = _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO)
 
  506             if isinstance(v, PropertySet):
 
  507                 d[name] = PropertySet.toDict(v)
 
  516         if len(self) != len(other):
 
  520             if _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO) != \
 
  521                     _propertyContainerGet(other, name, returnStyle=ReturnStyle.AUTO):
 
  523             if self.typeOf(name) != other.typeOf(name):
 
  531         for itemName 
in self:
 
  532             ps.copy(itemName, self, itemName)
 
  536         result = self.deepCopy()
 
  537         memo[
id(self)] = result
 
  541         """Determines if the name is found at the top level hierarchy 
  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__``. 
  550         return name 
in self.names(topLevelOnly=
True)
 
  553         """Assigns the supplied value to the container. 
  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. 
  566         Uses `PropertySet.set`, overwriting any previous value. 
  568         if isinstance(value, Mapping):
 
  571             for k, v 
in value.items():
 
  574         self.
set(name, value)
 
  577         """Returns a scalar item from the container. 
  581         Uses `PropertySet.getScalar` to guarantee that a single value 
  590             raise KeyError(f
"{name} not present in dict")
 
  593         return self.toString()
 
  596         return self.nameCount(topLevelOnly=
True)
 
  599         for n 
in self.names(topLevelOnly=
True):
 
  603         return KeysView(self)
 
  606         return ItemsView(self)
 
  609         return ValuesView(self)
 
  623     _typeMenu = {bool: 
"Bool",
 
  627                  DateTime: 
"DateTime",
 
  628                  PropertySet: 
"PropertySet",
 
  629                  PropertyList: 
"PropertySet",
 
  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.""" 
  637     def get(self, name, default=None):
 
  638         """Return an item as a scalar, else default. 
  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. 
  648         default : `object`, optional 
  649             Default value to use if the named item is not present. 
  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 
  660             return _propertyContainerGet(self, name, returnStyle=ReturnStyle.SCALAR)
 
  665         """Return an item as a list. 
  674         values : `list` of values 
  675             The contents of the item, guaranteed to be returned as a `list.` 
  680             Raised if the item does not exist. 
  682         return _propertyContainerGet(self, name, returnStyle=ReturnStyle.ARRAY)
 
  685         """Return an item as a scalar 
  687         If the item has more than one value then the last value is returned. 
  697             Value stored in the item.  If the item refers to an array the 
  698             most recently added value is returned. 
  703             Raised if the item does not exist. 
  705         return _propertyContainerGet(self, name, returnStyle=ReturnStyle.SCALAR)
 
  707     def set(self, name, value, comment=None):
 
  708         """Set the value of an item 
  710         If the item already exists it is silently replaced; the types 
  717         value : any supported type 
  718             Value of item; may be a scalar or array 
  721         if comment 
is not None:
 
  723         return _propertyContainerSet(self, name, value, self.
_typeMenu, *args)
 
  725     def add(self, name, value, comment=None):
 
  726         """Append one or more values to a given item, which need not exist 
  728         If the item exists then the new value(s) are appended; 
  729         otherwise it is like calling `set` 
  735         value : any supported type 
  736             Value of item; may be a scalar or array 
  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). 
  749         lsst::pex::exceptions::TypeError 
  750             Raise if the type of ``value`` is incompatible with the existing 
  754         if comment 
is not None:
 
  756         return _propertyContainerAdd(self, name, value, self.
_typeMenu, *args)
 
  759         """Set the comment for an existing entry. 
  764             Name of the key to receive updated comment. 
  770         containerType = _propertyContainerElementTypeName(self, name)
 
  771         if self.isArray(name):
 
  775         getattr(self, f
"set{containerType}")(name, value, comment)
 
  778         """Return a list of tuples of name, value, comment for each property 
  779         in the order that they were inserted. 
  783         ret : `list` of `tuple` 
  784             Tuples of name, value, comment for each property in the order 
  785             in which they were inserted. 
  787         orderedNames = self.getOrderedNames()
 
  789         for name 
in orderedNames:
 
  790             if self.isArray(name):
 
  791                 values = _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO)
 
  793                     ret.append((name, v, self.getComment(name)))
 
  795                 ret.append((name, _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO),
 
  796                             self.getComment(name)))
 
  800         """Return an ordered dictionary with all properties in the order that 
  806             Ordered dictionary with all properties in the order that they 
  807             were inserted. Comments are not included. 
  811         As of Python 3.6 dicts retain their insertion order. 
  815             d[name] = _propertyContainerGet(self, name, returnStyle=ReturnStyle.AUTO)
 
  819     toDict = toOrderedDict
 
  825         if not PropertySet.__eq__(self, other):
 
  829             if self.getComment(name) != other.getComment(name):
 
  837         for itemName 
in self:
 
  838             pl.copy(itemName, self, itemName)
 
  842         result = self.deepCopy()
 
  843         memo[
id(self)] = result
 
  847         for n 
in self.getOrderedNames():
 
  851         """Assigns the supplied value to the container. 
  856             Name of item to update. If the name ends with 
  857             `PropertyList.COMMENTSUFFIX`, the comment is updated rather 
  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. 
  866         Uses `PropertySet.set`, overwriting any previous value. 
  872         if isinstance(value, Mapping):
 
  875             for k, v 
in value.items():
 
  878         self.
set(name, value)