lsst.daf.base  14.0-3-g99bcfa4
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 from __future__ import absolute_import, division, print_function
25 
26 __all__ = ["getstate", "setstate"]
27 
28 from past.builtins import long
29 import numbers
30 
31 from lsst.utils import continueClass
32 
33 from .propertySet import PropertySet
34 from .propertyList import PropertyList
35 
37 from ..dateTime import DateTime
38 
39 
40 def _propertyContainerElementTypeName(container, name):
41  """Return name of the type of a particular element"""
42  t = container.typeOf(name)
43  for checkType in ("Bool", "Short", "Int", "Long", "LongLong", "Float", "Double", "String", "DateTime"):
44  if t == getattr(container, "TYPE_" + checkType):
45  return checkType
46  return None
47 
48 
49 def _propertyContainerGet(container, name, asArray=False):
50  """Extract a single Python value of unknown type"""
51  if not container.exists(name):
52  raise lsst.pex.exceptions.NotFoundError(name + " not found")
53 
54  elemType = _propertyContainerElementTypeName(container, name)
55  if elemType:
56  value = getattr(container, "getArray" + elemType)(name)
57  return value[0] if len(value) == 1 and not asArray else value
58 
59  try:
60  return container.getAsPropertyListPtr(name)
61  except:
62  pass
63  if container.typeOf(name) == container.TYPE_PropertySet:
64  return container.getAsPropertySetPtr(name)
65  try:
66  return container.getAsPersistablePtr(name)
67  except:
68  pass
69  raise lsst.pex.exceptions.TypeError('Unknown PropertySet value type for ' + name)
70 
71 
72 def _guessIntegerType(container, name, value):
73  """Given an existing container and name, determine the type
74  that should be used for the supplied value. The supplied value
75  is assumed to be a scalar.
76 
77  On Python 3 all ints are LongLong but we need to be able to store them
78  in Int containers if that is what is being used (testing for truncation).
79  Int is assumed to mean 32bit integer (2147483647 to -2147483648).
80 
81  If there is no pre-existing value we have to decide what to do. For now
82  we pick Int if the value is less than maxsize.
83 
84  Returns None if the value supplied is a bool or not an integral value.
85  """
86  useType = None
87  maxInt = 2147483647
88  minInt = -2147483648
89 
90  # We do not want to convert bool to int so let the system work that
91  # out itself
92  if isinstance(value, bool):
93  return useType
94 
95  if isinstance(value, numbers.Integral):
96  try:
97  containerType = _propertyContainerElementTypeName(container, name)
99  # nothing in the container so choose based on size. Safe option is to
100  # always use LongLong
101  if value <= maxInt and value >= minInt:
102  useType = "Int"
103  else:
104  useType = "LongLong"
105  else:
106  if containerType == "Int":
107  # Always use an Int even if we know it won't fit. The later
108  # code will trigger OverflowError if appropriate. Setting the
109  # type to LongLong here will trigger a TypeError instead so it's
110  # best to trigger a predictable OverflowError.
111  useType = "Int"
112  elif containerType == "LongLong":
113  useType = "LongLong"
114  return useType
115 
116 
117 def _propertyContainerSet(container, name, value, typeMenu, *args):
118  """Set a single Python value of unknown type"""
119  if hasattr(value, "__iter__") and not isinstance(value, str):
120  exemplar = value[0]
121  else:
122  exemplar = value
123 
124  t = type(exemplar)
125  setType = _guessIntegerType(container, name, exemplar)
126 
127  if setType is not None or t in typeMenu:
128  if setType is None:
129  setType = typeMenu[t]
130  return getattr(container, "set" + setType)(name, value, *args)
131  # Allow for subclasses
132  for checkType in typeMenu:
133  if isinstance(exemplar, checkType):
134  return getattr(container, "set" + typeMenu[checkType])(name, value, *args)
135  raise lsst.pex.exceptions.TypeError("Unknown value type for %s: %s" % (name, t))
136 
137 
138 def _propertyContainerAdd(container, name, value, typeMenu, *args):
139  """Add a single Python value of unknown type"""
140  if hasattr(value, "__iter__"):
141  exemplar = value[0]
142  else:
143  exemplar = value
144 
145  t = type(exemplar)
146  addType = _guessIntegerType(container, name, exemplar)
147 
148  if addType is not None or t in typeMenu:
149  if addType is None:
150  addType = typeMenu[t]
151  return getattr(container, "add" + addType)(name, value, *args)
152  # Allow for subclasses
153  for checkType in typeMenu:
154  if isinstance(exemplar, checkType):
155  return getattr(container, "add" + typeMenu[checkType])(name, value, *args)
156  raise lsst.pex.exceptions.TypeError("Unknown value type for %s: %s" % (name, t))
157 
158 
159 def getstate(self):
160  return [(name, _propertyContainerElementTypeName(self, name), self.get(name),
161  self.getComment(name)) for name in self.getOrderedNames()]
162 
163 
164 def setstate(self, state):
165  for name, elemType, value, comment in state:
166  getattr(self, "set" + elemType)(name, value, comment)
167 
168 
169 @continueClass
171  # Mapping of type to method names
172  _typeMenu = {bool: "Bool",
173  long: "LongLong",
174  int: "Int", # overwrites long on Python 3.x
175  float: "Double",
176  str: "String",
177  DateTime: "DateTime",
178  PropertySet: "PropertySet",
179  PropertyList: "PropertySet",
180  }
181 
182  # Map unicode to String, but this only works on Python 2
183  # so catch the error and do nothing on Python 3.
184  try:
185  _typeMenu[unicode] = "String" # noqa F821
186  except:
187  pass
188 
189  def get(self, name, asArray=False):
190  return _propertyContainerGet(self, name, asArray)
191 
192  def set(self, name, value):
193  return _propertyContainerSet(self, name, value, self._typeMenu)
194 
195  def add(self, name, value):
196  return _propertyContainerAdd(self, name, value, self._typeMenu)
197 
198  def toDict(self):
199  """Returns a (possibly nested) dictionary with all properties.
200  """
201 
202  d = {}
203  for name in self.names():
204  v = self.get(name)
205 
206  if isinstance(v, PropertySet):
207  d[name] = PropertySet.toDict(v)
208  else:
209  d[name] = v
210  return d
211 
212 
213 @continueClass
215  # Mapping of type to method names
216  _typeMenu = {bool: "Bool",
217  long: "LongLong",
218  int: "Int", # overwrites long on Python 3.x
219  float: "Double",
220  str: "String",
221  DateTime: "DateTime",
222  PropertySet: "PropertySet",
223  PropertyList: "PropertySet",
224  }
225 
226  # Map unicode to String, but this only works on Python 2
227  # so catch the error and do nothing on Python 3.
228  try:
229  _typeMenu[unicode] = "String" # noqa F821
230  except:
231  pass
232 
233  def get(self, name, asArray=False):
234  return _propertyContainerGet(self, name, asArray)
235 
236  def set(self, name, value, comment=None):
237  args = []
238  if comment is not None:
239  args.append(comment)
240  return _propertyContainerSet(self, name, value, self._typeMenu, *args)
241 
242  def add(self, name, value, comment=None):
243  args = []
244  if comment is not None:
245  args.append(comment)
246  return _propertyContainerAdd(self, name, value, self._typeMenu, *args)
247 
248  def toList(self):
249  orderedNames = self.getOrderedNames()
250  ret = []
251  for name in orderedNames:
252  if self.isArray(name):
253  values = self.get(name)
254  for v in values:
255  ret.append((name, v, self.getComment(name)))
256  else:
257  ret.append((name, self.get(name), self.getComment(name)))
258  return ret
259 
260  def toOrderedDict(self):
261  """Return an ordered dictionary with all properties in the order that
262  they were inserted.
263  """
264  from collections import OrderedDict
265 
266  d = OrderedDict()
267  for name in self.getOrderedNames():
268  d[name] = self.get(name)
269  return d