LSST Applications g0f08755f38+82efc23009,g12f32b3c4e+e7bdf1200e,g1653933729+a8ce1bb630,g1a0ca8cf93+50eff2b06f,g28da252d5a+52db39f6a5,g2bbee38e9b+37c5a29d61,g2bc492864f+37c5a29d61,g2cdde0e794+c05ff076ad,g3156d2b45e+41e33cbcdc,g347aa1857d+37c5a29d61,g35bb328faa+a8ce1bb630,g3a166c0a6a+37c5a29d61,g3e281a1b8c+fb992f5633,g414038480c+7f03dfc1b0,g41af890bb2+11b950c980,g5fbc88fb19+17cd334064,g6b1c1869cb+12dd639c9a,g781aacb6e4+a8ce1bb630,g80478fca09+72e9651da0,g82479be7b0+04c31367b4,g858d7b2824+82efc23009,g9125e01d80+a8ce1bb630,g9726552aa6+8047e3811d,ga5288a1d22+e532dc0a0b,gae0086650b+a8ce1bb630,gb58c049af0+d64f4d3760,gc28159a63d+37c5a29d61,gcf0d15dbbd+2acd6d4d48,gd7358e8bfb+778a810b6e,gda3e153d99+82efc23009,gda6a2b7d83+2acd6d4d48,gdaeeff99f8+1711a396fd,ge2409df99d+6b12de1076,ge79ae78c31+37c5a29d61,gf0baf85859+d0a5978c5a,gf3967379c6+4954f8c433,gfb92a5be7c+82efc23009,gfec2e1e490+2aaed99252,w.2024.46
LSST Data Management Base Package
Loading...
Searching...
No Matches
_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
24import numpy as np
25import fnmatch
26import re
27import collections
28import astropy.units
29
30import lsst.geom
31from lsst.utils import continueClass, TemplateMeta
32
33from ._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
49class Key(metaclass=TemplateMeta):
50 pass
51
52
53class Field(metaclass=TemplateMeta):
54 pass
55
56
57class SchemaItem(metaclass=TemplateMeta):
58 pass
59
60
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.
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.
85Key.alias(float, _Key["D"])
86Field.alias(float, _Field["D"])
87SchemaItem.alias(float, _SchemaItem["D"])
88
89
90@continueClass
91class Schema: # noqa: F811
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 units : `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, **kwargs):
165 """Extract a dictionary of {<name>: <schema-item>} in which the field
166 names match the given shell-style glob pattern(s).
167
168 Any number of glob patterns may be passed; the result will be the
169 union of all 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 kwargs : `dict`
176 Dictionary of additional keyword arguments. May contain:
177
178 ``regex`` : `str` or `re` pattern
179 A regular expression to be used in addition to any
180 glob patterns passed as positional arguments. Note
181 that this will be compared with re.match, not
182 re.search.
183 ``sub`` : `str`
184 A replacement string (see re.MatchObject.expand) used
185 to set the dictionary keys of any fields matched by
186 regex.
187 ``ordered`` : `bool`, optional
188 If True, a collections.OrderedDict will be returned
189 instead of a standard dict, with the order
190 corresponding to the definition order of the
191 Schema. Default is False.
192
193 Returns
194 -------
195 d : `dict`
196 Dictionary of extracted name-schema item sets.
197
198 Raises
199 ------
200 ValueError
201 Raised if the `sub` keyword argument is invalid without
202 the `regex` argument.
203
204 Also raised if an unknown keyword argument is supplied.
205 """
206 if kwargs.pop("ordered", False):
207 d = collections.OrderedDict()
208 else:
209 d = dict()
210 regex = kwargs.pop("regex", None)
211 sub = kwargs.pop("sub", None)
212 if sub is not None and regex is None:
213 raise ValueError(
214 "'sub' keyword argument to extract is invalid without 'regex' argument")
215 if kwargs:
216 kwargsStr = ", ".join(kwargs.keys())
217 raise ValueError(f"Unrecognized keyword arguments for extract: {kwargsStr}")
218 for item in self:
219 trueName = item.field.getName()
220 names = [trueName]
221 for alias, target in self.getAliasMap().items():
222 if trueName.startswith(target):
223 names.append(trueName.replace(target, alias, 1))
224 for name in names:
225 if regex is not None:
226 m = re.match(regex, name)
227 if m is not None:
228 if sub is not None:
229 name = m.expand(sub)
230 d[name] = item
231 continue # continue middle loop so we don't match the same name twice
232 for pattern in patterns:
233 if fnmatch.fnmatchcase(name, pattern):
234 d[name] = item
235 break # break inner loop so we don't match the same name twice
236 return d
237
238 def __reduce__(self):
239 """For pickle support."""
240 fields = []
241 for item in self:
242 fields.append(item.field)
243 return (makeSchemaFromFields, (fields,))
244
245
247 """Create a Schema from a sequence of Fields. For pickle support.
248
249 Parameters
250 ----------
251 fields : `tuple` ['lsst.afw.table.Field']
252 The fields to construct the new Schema from.
253
254 Returns
255 -------
256 schema : `lsst.afw.table.Schema`
257 The constructed Schema.
258 """
259 schema = Schema()
260 for field in fields:
261 schema.addField(field)
262 return schema
std::vector< SchemaItem< Flag > > * items
addField(self, field, type=None, doc="", units="", size=None, doReplace=False, parse_strict="raise")
Definition _schema.py:130
checkUnits(self, parse_strict='raise')
Definition _schema.py:115
extract(self, *patterns, **kwargs)
Definition _schema.py:164
Tag types used to declare specialized field types.
Definition misc.h:31
A class representing an angle.
Definition Angle.h:128
_registerInstantiations(abc, types)
Definition _schema.py:61