LSSTApplications  18.1.0
LSSTDataManagementBasePackage
schemaContinued.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2017 LSST/AURA.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 
23 __all__ = ["Key", "Field", "SchemaItem"]
24 
25 import numpy as np
26 import fnmatch
27 import re
28 import collections
29 import astropy.units
30 
31 import lsst.geom
32 from lsst.utils import continueClass, TemplateMeta
33 
34 from .schema import _Key, _Field, _SchemaItem, Schema
35 
36 # Objects we prefer to use over the C++ string name for
37 # Key/Field/SchemaItem types.
38 _dtypes = {
39  "String": str,
40  "B": np.uint8,
41  "U": np.uint16,
42  "I": np.int32,
43  "L": np.int64,
44  "F": np.float32,
45  "D": np.float64,
46  "Angle": lsst.geom.Angle,
47 }
48 
49 
50 class Key(metaclass=TemplateMeta):
51  pass
52 
53 
54 class Field(metaclass=TemplateMeta):
55  pass
56 
57 
58 class SchemaItem(metaclass=TemplateMeta):
59  pass
60 
61 
62 def _registerInstantiations(abc, types):
63  """Iterate over a private dict (filled by template instantiations in C++)
64  to register template instantiations a TemplateMeta ABCs.
65 
66  If an entry for the type string exists in _dtypes, we use that instead of
67  the string as the key, and use the string as an alias.
68  """
69  for k, v in types.items():
70  dtype = _dtypes.get(k, None)
71  if dtype is not None:
72  abc.register(dtype, v)
73  abc.alias(k, v)
74  else:
75  abc.register(k, v)
76 
77 
78 # _Key, _Field, and _SchemaItem are {str: class} dicts populated by the C++
79 # wrappers. The keys are a superset of those in _dtypes.
80 _registerInstantiations(Key, _Key)
81 _registerInstantiations(Field, _Field)
82 _registerInstantiations(SchemaItem, _SchemaItem)
83 
84 # Also register `float->D` as an alias; can't include
85 # in _dtypes because we have (and prefer) np.float64 there.
86 Key.alias(float, _Key["D"])
87 Field.alias(float, _Field["D"])
88 SchemaItem.alias(float, _SchemaItem["D"])
89 
90 
91 @continueClass # noqa: F811
92 class Schema:
93 
94  def getOrderedNames(self):
95  """Return a list of field names in the order the fields were added to the Schema.
96  """
97  names = []
98 
99  def func(item):
100  names.append(item.field.getName())
101  self.forEach(func)
102  return names
103 
104  def __iter__(self):
105  """Iterate over the items in the Schema.
106  """
107  items = []
108  self.forEach(items.append)
109  return iter(items)
110 
111  def checkUnits(self, parse_strict='raise'):
112  """Check that all units in the Schema are valid Astropy unit strings.
113  """
114  def func(item):
115  astropy.units.Unit(item.field.getUnits(),
116  parse_strict=parse_strict)
117  self.forEach(func)
118 
119  def addField(self, field, type=None, doc="", units="", size=None,
120  doReplace=False, parse_strict="raise"):
121  """Add a field to the Schema.
122 
123  Parameters
124  ----------
125  field : str,Field
126  The string name of the Field, or a fully-constructed Field object.
127  If the latter, all other arguments besides doReplace are ignored.
128  type\n : str,type
129  The type of field to create. Valid types are the keys of the
130  afw.table.Field dictionary.
131  doc : str
132  Documentation for the field.
133  unit : str
134  Units for the field, or an empty string if unitless.
135  size : int
136  Size of the field; valid for string and array fields only.
137  doReplace : bool
138  If a field with this name already exists, replace it instead of
139  raising pex.exceptions.InvalidParameterError.
140  parse_strict : str
141  One of 'raise' (default), 'warn', or 'strict', indicating how to
142  handle unrecognized unit strings. See also astropy.units.Unit.
143  """
144  if isinstance(field, str):
145  field = Field[type](field, doc=doc, units=units,
146  size=size, parse_strict=parse_strict)
147  return field._addTo(self, doReplace)
148 
149  def extract(self, *patterns, **kwds):
150  """
151  Extract a dictionary of {<name>: <schema-item>} in which the field names
152  match the given shell-style glob pattern(s).
153 
154  Any number of glob patterns may be passed; the result will be the union of all
155  the result of each glob considered separately.
156 
157  Additional Keyword Arguments
158  ----------------------------
159  regex : `str` or `re` pattern
160  A regular expression to be used in addition to any glob patterns
161  passed as positional arguments. Note that this will be compared
162  with re.match, not re.search.
163 
164  sub : `str`
165  A replacement string (see re.MatchObject.expand) used to set the
166  dictionary keys of any fields matched by regex.
167 
168  ordered : `bool`
169  If True, a collections.OrderedDict will be returned instead of a
170  standard dict, with the order corresponding to the definition
171  order of the Schema. Default is False.
172  """
173  if kwds.pop("ordered", False):
174  d = collections.OrderedDict()
175  else:
176  d = dict()
177  regex = kwds.pop("regex", None)
178  sub = kwds.pop("sub", None)
179  if sub is not None and regex is None:
180  raise ValueError(
181  "'sub' keyword argument to extract is invalid without 'regex' argument")
182  if kwds:
183  raise ValueError(
184  "Unrecognized keyword arguments for extract: %s" % ", ".join(kwds.keys()))
185  for item in self:
186  trueName = item.field.getName()
187  names = [trueName]
188  for alias, target in self.getAliasMap().items():
189  if trueName.startswith(target):
190  names.append(trueName.replace(target, alias, 1))
191  for name in names:
192  if regex is not None:
193  m = re.match(regex, name)
194  if m is not None:
195  if sub is not None:
196  name = m.expand(sub)
197  d[name] = item
198  continue # continue middle loop so we don't match the same name twice
199  for pattern in patterns:
200  if fnmatch.fnmatchcase(name, pattern):
201  d[name] = item
202  break # break inner loop so we don't match the same name twice
203  return d
204 
205  def __reduce__(self):
206  """For pickle support."""
207  fields = []
208  for item in self:
209  fields.append(item.field)
210  return (makeSchemaFromFields, (fields,))
211 
212 
214  """Create a Schema from a sequence of Fields. For pickle support.
215 
216  Parameters
217  ----------
218  fields : `tuple` ['lsst.afw.table.Field']
219  The fields to construct the new Schema from.
220 
221  Returns
222  -------
223  schema : `lsst.afw.table.Schema`
224  The constructed Schema.
225  """
226  schema = Schema()
227  for field in fields:
228  schema.addField(field)
229  return schema
A class representing an angle.
Definition: Angle.h:127
def addField(self, field, type=None, doc="", units="", size=None, doReplace=False, parse_strict="raise")
std::vector< SchemaItem< Flag > > * items
def checkUnits(self, parse_strict='raise')