LSSTApplications  17.0+124,17.0+14,17.0+73,18.0.0+37,18.0.0+80,18.0.0-4-g68ffd23+4,18.1.0-1-g0001055+12,18.1.0-1-g03d53ef+5,18.1.0-1-g1349e88+55,18.1.0-1-g2505f39+44,18.1.0-1-g5315e5e+4,18.1.0-1-g5e4b7ea+14,18.1.0-1-g7e8fceb+4,18.1.0-1-g85f8cd4+48,18.1.0-1-g8ff0b9f+4,18.1.0-1-ga2c679d+1,18.1.0-1-gd55f500+35,18.1.0-10-gb58edde+2,18.1.0-11-g0997b02+4,18.1.0-13-gfe4edf0b+12,18.1.0-14-g259bd21+21,18.1.0-19-gdb69f3f+2,18.1.0-2-g5f9922c+24,18.1.0-2-gd3b74e5+11,18.1.0-2-gfbf3545+32,18.1.0-26-g728bddb4+5,18.1.0-27-g6ff7ca9+2,18.1.0-3-g52aa583+25,18.1.0-3-g8ea57af+9,18.1.0-3-gb69f684+42,18.1.0-3-gfcaddf3+6,18.1.0-32-gd8786685a,18.1.0-4-gf3f9b77+6,18.1.0-5-g1dd662b+2,18.1.0-5-g6dbcb01+41,18.1.0-6-gae77429+3,18.1.0-7-g9d75d83+9,18.1.0-7-gae09a6d+30,18.1.0-9-gc381ef5+4,w.2019.45
LSSTDataManagementBasePackage
_schema.py
Go to the documentation of this file.
1 # This file is part of afw.
2 #
3 # Developed for the LSST Data Management System.
4 # This product includes software developed by the LSST Project
5 # (https://www.lsst.org).
6 # See the COPYRIGHT file at the top-level directory of this distribution
7 # for details of code ownership.
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 GNU General Public License
20 # along with this program. If not, see <https://www.gnu.org/licenses/>.
21 
22 __all__ = ["Key", "Field", "SchemaItem"]
23 
24 import numpy as np
25 import fnmatch
26 import re
27 import collections
28 import astropy.units
29 
30 import lsst.geom
31 from lsst.utils import continueClass, TemplateMeta
32 
33 from ._table import _Key, _Field, _SchemaItem, Schema
34 
35 # Objects we prefer to use over the C++ string name for
36 # Key/Field/SchemaItem types.
37 _dtypes = {
38  "String": str,
39  "B": np.uint8,
40  "U": np.uint16,
41  "I": np.int32,
42  "L": np.int64,
43  "F": np.float32,
44  "D": np.float64,
45  "Angle": lsst.geom.Angle,
46 }
47 
48 
49 class Key(metaclass=TemplateMeta):
50  pass
51 
52 
53 class Field(metaclass=TemplateMeta):
54  pass
55 
56 
57 class SchemaItem(metaclass=TemplateMeta):
58  pass
59 
60 
61 def _registerInstantiations(abc, types):
62  """Iterate over a private dict (filled by template instantiations in C++)
63  to register template instantiations a TemplateMeta ABCs.
64 
65  If an entry for the type string exists in _dtypes, we use that instead of
66  the string as the key, and use the string as an alias.
67  """
68  for k, v in types.items():
69  dtype = _dtypes.get(k, None)
70  if dtype is not None:
71  abc.register(dtype, v)
72  abc.alias(k, v)
73  else:
74  abc.register(k, v)
75 
76 
77 # _Key, _Field, and _SchemaItem are {str: class} dicts populated by the C++
78 # wrappers. The keys are a superset of those in _dtypes.
79 _registerInstantiations(Key, _Key)
80 _registerInstantiations(Field, _Field)
81 _registerInstantiations(SchemaItem, _SchemaItem)
82 
83 # Also register `float->D` as an alias; can't include
84 # in _dtypes because we have (and prefer) np.float64 there.
85 Key.alias(float, _Key["D"])
86 Field.alias(float, _Field["D"])
87 SchemaItem.alias(float, _SchemaItem["D"])
88 
89 
90 @continueClass # noqa: F811
91 class Schema:
92 
93  def getOrderedNames(self):
94  """Return a list of field names in the order the fields were added to the Schema.
95 
96  Returns
97  -------
98  names : `List`
99  Field names in order they were added to the Schema.
100  """
101  names = []
102 
103  def func(item):
104  names.append(item.field.getName())
105  self.forEach(func)
106  return names
107 
108  def __iter__(self):
109  """Iterate over the items in the Schema.
110  """
111  items = []
112  self.forEach(items.append)
113  return iter(items)
114 
115  def checkUnits(self, parse_strict='raise'):
116  """Check that all units in the Schema are valid Astropy unit strings.
117 
118  Parameters
119  ----------
120  parse_strict : `str`, optional
121  One of 'raise' (default), 'warn', or 'strict', indicating how to
122  handle unrecognized unit strings. See also astropy.units.Unit.
123  """
124  def func(item):
125  astropy.units.Unit(item.field.getUnits(),
126  parse_strict=parse_strict)
127  self.forEach(func)
128 
129  def addField(self, field, type=None, doc="", units="", size=None,
130  doReplace=False, parse_strict="raise"):
131  """Add a field to the Schema.
132 
133  Parameters
134  ----------
135  field : `str` or `Field`
136  The string name of the Field, or a fully-constructed Field object.
137  If the latter, all other arguments besides doReplace are ignored.
138  type : `str`, optional
139  The type of field to create. Valid types are the keys of the
140  afw.table.Field dictionary.
141  doc : `str`
142  Documentation for the field.
143  unit : `str`
144  Units for the field, or an empty string if unitless.
145  size : `int`
146  Size of the field; valid for string and array fields only.
147  doReplace : `bool`
148  If a field with this name already exists, replace it instead of
149  raising pex.exceptions.InvalidParameterError.
150  parse_strict : `str`
151  One of 'raise' (default), 'warn', or 'strict', indicating how to
152  handle unrecognized unit strings. See also astropy.units.Unit.
153 
154  Returns
155  -------
156  result :
157  Result of the `Field` addition.
158  """
159  if isinstance(field, str):
160  field = Field[type](field, doc=doc, units=units,
161  size=size, parse_strict=parse_strict)
162  return field._addTo(self, doReplace)
163 
164  def extract(self, *patterns, **kwds):
165  """Extract a dictionary of {<name>: <schema-item>} in which the field names
166  match the given shell-style glob pattern(s).
167 
168  Any number of glob patterns may be passed; the result will be the union of all
169  the result of each glob considered separately.
170 
171  Parameters
172  ----------
173  patterns : Array of `str`
174  List of glob patterns to use to select field names.
175  kwds : `dict`
176  Dictionary of additional keyword arguments. May contain:
177  - ``regex`` : `str` or `re` pattern
178  A regular expression to be used in addition to any
179  glob patterns passed as positional arguments. Note
180  that this will be compared with re.match, not
181  re.search.
182  - ``sub`` : `str`
183  A replacement string (see re.MatchObject.expand) used
184  to set the dictionary keys of any fields matched by
185  regex.
186  - ``ordered`` : `bool`, optional
187  If True, a collections.OrderedDict will be returned
188  instead of a standard dict, with the order
189  corresponding to the definition order of the
190  Schema. Default is False.
191 
192  Returns
193  -------
194  d : `dict`
195  Dictionary of extracted name-schema item sets.
196 
197  Raises
198  ------
199  ValueError
200  Raised if the `sub` keyword argument is invalid without
201  the `regex` argument.
202 
203  Also raised if an unknown keyword argument is supplied.
204  """
205  if kwds.pop("ordered", False):
206  d = collections.OrderedDict()
207  else:
208  d = dict()
209  regex = kwds.pop("regex", None)
210  sub = kwds.pop("sub", None)
211  if sub is not None and regex is None:
212  raise ValueError(
213  "'sub' keyword argument to extract is invalid without 'regex' argument")
214  if kwds:
215  raise ValueError(
216  "Unrecognized keyword arguments for extract: %s" % ", ".join(kwds.keys()))
217  for item in self:
218  trueName = item.field.getName()
219  names = [trueName]
220  for alias, target in self.getAliasMap().items():
221  if trueName.startswith(target):
222  names.append(trueName.replace(target, alias, 1))
223  for name in names:
224  if regex is not None:
225  m = re.match(regex, name)
226  if m is not None:
227  if sub is not None:
228  name = m.expand(sub)
229  d[name] = item
230  continue # continue middle loop so we don't match the same name twice
231  for pattern in patterns:
232  if fnmatch.fnmatchcase(name, pattern):
233  d[name] = item
234  break # break inner loop so we don't match the same name twice
235  return d
236 
237  def __reduce__(self):
238  """For pickle support."""
239  fields = []
240  for item in self:
241  fields.append(item.field)
242  return (makeSchemaFromFields, (fields,))
243 
244 
246  """Create a Schema from a sequence of Fields. For pickle support.
247 
248  Parameters
249  ----------
250  fields : `tuple` ['lsst.afw.table.Field']
251  The fields to construct the new Schema from.
252 
253  Returns
254  -------
255  schema : `lsst.afw.table.Schema`
256  The constructed Schema.
257  """
258  schema = Schema()
259  for field in fields:
260  schema.addField(field)
261  return schema
std::vector< SchemaItem< Flag > > * items
def makeSchemaFromFields(fields)
Definition: _schema.py:245
def extract(self, patterns, kwds)
Definition: _schema.py:164
A class representing an angle.
Definition: Angle.h:127
def checkUnits(self, parse_strict='raise')
Definition: _schema.py:115
def addField(self, field, type=None, doc="", units="", size=None, doReplace=False, parse_strict="raise")
Definition: _schema.py:130