LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
_schema.cc
Go to the documentation of this file.
1 /*
2  * This file is part of afw.
3  *
4  * Developed for the LSST Data Management System.
5  * This product includes software developed by the LSST Project
6  * (https://www.lsst.org).
7  * See the COPYRIGHT file at the top-level directory of this distribution
8  * for details of code ownership.
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program. If not, see <https://www.gnu.org/licenses/>.
22  */
23 
24 #include "pybind11/pybind11.h"
25 #include "pybind11/stl.h"
26 
27 #include <sstream>
28 
29 #include "ndarray/pybind11.h"
30 
31 #include "lsst/utils/python.h"
32 
33 #include "lsst/afw/fits.h"
35 #include "lsst/afw/table/Schema.h"
38 
39 namespace py = pybind11;
40 using namespace pybind11::literals;
41 
42 namespace lsst {
43 namespace afw {
44 namespace table {
45 
46 using utils::python::WrapperCollection;
47 
48 namespace {
49 
50 using PySchema = py::class_<Schema>;
51 
52 using PySubSchema = py::class_<SubSchema>;
53 
54 template <typename T>
55 using PyFieldBase = py::class_<FieldBase<T>>;
56 
57 template <typename T>
58 using PyKeyBase = py::class_<KeyBase<T>>;
59 
60 template <typename T>
61 using PyField = py::class_<Field<T>, FieldBase<T>>;
62 
63 template <typename T>
64 using PyKey = py::class_<Key<T>, KeyBase<T>, FieldBase<T>>;
65 
66 template <typename T>
67 using PySchemaItem = py::class_<SchemaItem<T>>;
68 
69 // Specializations for FieldBase
70 
71 template <typename T>
72 void declareFieldBaseSpecializations(PyFieldBase<T> &cls) {
73  cls.def(py::init<>());
74 }
75 
76 template <typename T>
77 void declareFieldBaseSpecializations(PyFieldBase<Array<T>> &cls) {
78  cls.def(py::init<int>(), "size"_a = 0);
79  cls.def("getSize", &FieldBase<Array<T>>::getSize);
80  cls.def("isVariableLength", &FieldBase<Array<T>>::isVariableLength);
81 }
82 
83 void declareFieldBaseSpecializations(PyFieldBase<std::string> &cls) {
84  cls.def(py::init<int>(), "size"_a = -1);
85  cls.def("getSize", &FieldBase<std::string>::getSize);
86 }
87 
88 // Specializations for Field
89 
90 template <typename T>
91 void declareFieldSpecializations(PyField<T> &cls) {
92  cls.def(py::pickle(
93  [](Field<T> const &self) {
94  /* Return a tuple that fully encodes the state of the object */
95  return py::make_tuple(self.getName(), self.getDoc(), self.getUnits());
96  },
97  [](py::tuple t) {
98  int const NPARAMS = 3;
99  if (t.size() != NPARAMS) {
101  os << "Invalid number of parameters (" << t.size() << ") when unpickling; expected "
102  << NPARAMS;
103  throw std::runtime_error(os.str());
104  }
105  return Field<T>(t[0].cast<std::string>(), t[1].cast<std::string>(), t[2].cast<std::string>());
106  }));
107 }
108 
109 // Field<Array<T>> and Field<std::string> have the same pickle implementation
110 template <typename T>
111 void _sequenceFieldSpecializations(PyField<T> &cls) {
112  cls.def(py::pickle(
113  [](Field<T> const &self) {
114  /* Return a tuple that fully encodes the state of the object */
115  return py::make_tuple(self.getName(), self.getDoc(), self.getUnits(), self.getSize());
116  },
117  [](py::tuple t) {
118  int const NPARAMS = 4;
119  if (t.size() != NPARAMS) {
121  os << "Invalid number of parameters (" << t.size() << ") when unpickling; expected "
122  << NPARAMS;
123  throw std::runtime_error(os.str());
124  }
125  return Field<T>(t[0].cast<std::string>(), t[1].cast<std::string>(), t[2].cast<std::string>(),
126  t[3].cast<int>());
127  }));
128 }
129 
130 template <typename T>
131 void declareFieldSpecializations(PyField<Array<T>> &cls) {
132  _sequenceFieldSpecializations(cls);
133 }
134 
135 void declareFieldSpecializations(PyField<std::string> &cls) { _sequenceFieldSpecializations(cls); }
136 
137 // Specializations for KeyBase
138 
139 template <typename T>
140 void declareKeyBaseSpecializations(PyKeyBase<T> &) {}
141 
142 template <typename T>
143 void declareKeyBaseSpecializations(PyKeyBase<Array<T>> &cls) {
144  cls.def("__getitem__", [](Key<Array<T>> const &self, py::object const &index) -> py::object {
145  if (py::isinstance<py::slice>(index)) {
146  py::slice slice(index);
147  py::size_t start = 0, stop = 0, step = 0, length = 0;
148  bool valid = slice.compute(self.getSize(), &start, &stop, &step, &length);
149  if (!valid) throw py::error_already_set();
150  if (step != 1) {
151  throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
152  "Step for array Key indexing must be 1.");
153  }
154  return py::cast(self.slice(start, stop));
155  } else {
156  return py::cast(self[py::cast<int>(index)]);
157  }
158  });
159  cls.def("slice", &KeyBase<Array<T>>::slice);
160 }
161 
162 // Specializations for Key
163 
164 template <typename T>
165 void declareKeyAccessors(PyKey<T> &cls) {
166  cls.def("get", [](Key<T> const &self, BaseRecord &record) { return record.get(self); });
167  cls.def("set", [](Key<T> const &self, BaseRecord &record, typename Key<T>::Value const &value) {
168  record.set(self, value);
169  });
170 }
171 
172 template <typename U>
173 void declareKeyAccessors(PyKey<Array<U>> &cls) {
174  auto getter = [](Key<Array<U>> const &self, BaseRecord &record) -> ndarray::Array<U, 1, 1> {
175  return record[self];
176  };
177  auto setter = [](Key<Array<U>> const &self, BaseRecord &record, py::object const &value) {
178  if (self.getSize() == 0) {
179  // Variable-length array field: do a shallow copy, which requires a non-const
180  // contiguous array.
181  record.set(self, py::cast<ndarray::Array<U, 1, 1>>(value));
182  } else {
183  // Fixed-length array field: do a deep copy, which can work with a const
184  // noncontiguous array. But we need to check the size first, since the
185  // penalty for getting that wrong is assert->abort.
186  auto v = py::cast<ndarray::Array<U const, 1, 0>>(value);
187  ndarray::ArrayRef<U, 1, 1> ref = record[self];
188  if (v.size() != ref.size()) {
189  throw LSST_EXCEPT(
190  pex::exceptions::LengthError,
191  (boost::format("Array sizes do not agree: %s != %s") % v.size() % ref.size()).str());
192  }
193  ref = v;
194  }
195  return;
196  };
197  cls.def("get", getter);
198  cls.def("set", setter);
199 }
200 
201 template <typename T>
202 void declareKeySpecializations(PyKey<T> &cls) {
203  declareKeyAccessors(cls);
204  cls.def_property_readonly("subfields", [](py::object const &) { return py::none(); });
205  cls.def_property_readonly("subkeys", [](py::object const &) { return py::none(); });
206  cls.def(py::pickle(
207  [](Key<T> const &self) {
208  /* Return a tuple that fully encodes the state of the object */
209  return py::make_tuple(self.getOffset());
210  },
211  [](py::tuple t) {
212  int const NPARAMS = 1;
213  if (t.size() != NPARAMS) {
215  os << "Invalid number of parameters (" << t.size() << ") when unpickling; expected "
216  << NPARAMS;
217  throw std::runtime_error(os.str());
218  }
219  return detail::Access::makeKey<T>(t[0].cast<int>());
220  }));
221 }
222 
223 void declareKeySpecializations(PyKey<Flag> &cls) {
224  declareKeyAccessors(cls);
225  cls.def_property_readonly("subfields", [](py::object const &) { return py::none(); });
226  cls.def_property_readonly("subkeys", [](py::object const &) { return py::none(); });
227  cls.def("getBit", &Key<Flag>::getBit);
228  cls.def(py::pickle(
229  [](Key<Flag> const &self) {
230  /* Return a tuple that fully encodes the state of the object */
231  return py::make_tuple(self.getOffset(), self.getBit());
232  },
233  [](py::tuple t) {
234  int const NPARAMS = 2;
235  if (t.size() != NPARAMS) {
237  os << "Invalid number of parameters (" << t.size() << ") when unpickling; expected "
238  << NPARAMS;
239  throw std::runtime_error(os.str());
240  }
241  return detail::Access::makeKey(t[0].cast<int>(), t[1].cast<int>());
242  }));
243 }
244 
245 template <typename T>
246 void declareKeySpecializations(PyKey<Array<T>> &cls) {
247  declareKeyAccessors(cls);
248  cls.def_property_readonly("subfields", [](Key<Array<T>> const &self) -> py::object {
250  for (std::size_t i = 0; i < self.getSize(); ++i) {
251  result.append(py::cast(i));
252  }
253  return py::tuple(result);
254  });
255  cls.def_property_readonly("subkeys", [](Key<Array<T>> const &self) -> py::object {
257  for (std::size_t i = 0; i < self.getSize(); ++i) {
258  result.append(py::cast(self[i]));
259  }
260  return py::tuple(result);
261  });
262  cls.def(py::pickle(
263  [](Key<Array<T>> const &self) {
264  /* Return a tuple that fully encodes the state of the object */
265  return py::make_tuple(self.getOffset(), self.getElementCount());
266  },
267  [](py::tuple t) {
268  int const NPARAMS = 2;
269  if (t.size() != NPARAMS) {
271  os << "Invalid number of parameters (" << t.size() << ") when unpickling; expected "
272  << NPARAMS;
273  throw std::runtime_error(os.str());
274  }
275  return detail::Access::makeKeyArray<T>(t[0].cast<int>(), t[1].cast<int>());
276  }));
277 }
278 
279 void declareKeySpecializations(PyKey<std::string> &cls) {
280  declareKeyAccessors(cls);
281  cls.def_property_readonly("subfields", [](py::object const &) { return py::none(); });
282  cls.def_property_readonly("subkeys", [](py::object const &) { return py::none(); });
283  cls.def(py::pickle(
284  [](Key<std::string> const &self) {
285  /* Return a tuple that fully encodes the state of the object */
286  return py::make_tuple(self.getOffset(), self.getElementCount());
287  },
288  [](py::tuple t) {
289  int const NPARAMS = 2;
290  if (t.size() != NPARAMS) {
292  os << "Invalid number of parameters (" << t.size() << ") when unpickling; expected "
293  << NPARAMS;
294  throw std::runtime_error(os.str());
295  }
296  return detail::Access::makeKeyString(t[0].cast<int>(), t[1].cast<int>());
297  }));
298 }
299 
300 // Wrap all helper classes (FieldBase, KeyBase, Key, Field, SchemaItem) declarefor a Schema field type.
301 template <typename T>
302 void declareSchemaType(WrapperCollection &wrappers) {
303  std::string suffix = FieldBase<T>::getTypeString();
304  py::str pySuffix(suffix);
305 
306  py::object astropyUnit = py::module::import("astropy.units").attr("Unit");
307 
308  // FieldBase
309  wrappers.wrapType(PyFieldBase<T>(wrappers.module, ("FieldBase" + suffix).c_str()),
310  [](auto &mod, auto &cls) {
311  cls.def_static("getTypeString", &FieldBase<T>::getTypeString);
312  declareFieldBaseSpecializations(cls);
313  });
314 
315  // KeyBase
316  wrappers.wrapType(PyKeyBase<T>(wrappers.module, ("KeyBase" + suffix).c_str()), [](auto &mod, auto &cls) {
317  declareKeyBaseSpecializations(cls);
318  });
319 
320  // Field
321  wrappers.wrapType(PyField<T>(wrappers.module, ("Field" + suffix).c_str()), [pySuffix, astropyUnit](
322  auto &mod, auto &cls) {
323  declareFieldSpecializations(cls);
324 
325  mod.attr("_Field")[pySuffix] = cls;
326 
327  cls.def(py::init([astropyUnit]( // capture by value to refcount in Python instead of dangle in C++
328  std::string const &name, std::string const &doc, py::str const &units,
329  py::object const &size, py::str const &parse_strict) {
330  astropyUnit(units, "parse_strict"_a = parse_strict);
331  std::string u = py::cast<std::string>(units);
332  if (size.is(py::none())) {
333  return new Field<T>(name, doc, u);
334  } else {
335  int s = py::cast<int>(size);
336  return new Field<T>(name, doc, u, s);
337  }
338  }),
339  "name"_a, "doc"_a = "", "units"_a = "", "size"_a = py::none(), "parse_strict"_a = "raise");
340  cls.def("_addTo", [](Field<T> const &self, Schema &schema, bool doReplace) -> Key<T> {
341  return schema.addField(self, doReplace);
342  });
343  cls.def("getName", &Field<T>::getName);
344  cls.def("getDoc", &Field<T>::getDoc);
345  cls.def("getUnits", &Field<T>::getUnits);
346  cls.def("copyRenamed", &Field<T>::copyRenamed);
347  utils::python::addOutputOp(cls, "__str__");
348  utils::python::addOutputOp(cls, "__repr__");
349  });
350 
351  // Key
352  wrappers.wrapType(PyKey<T>(wrappers.module, ("Key" + suffix).c_str()), [pySuffix](auto &mod, auto &cls) {
353  mod.attr("_Key")[pySuffix] = cls;
354  cls.def(py::init<>());
355  cls.def("__eq__", [](Key<T> const &self, Key<T> const &other) -> bool { return self == other; },
356  py::is_operator());
358  cls.def("__ne__", [](Key<T> const &self, Key<T> const &other) -> bool { return self != other; },
359  py::is_operator());
360  cls.def("isValid", &Key<T>::isValid);
361  cls.def("getOffset", &Key<T>::getOffset);
362  utils::python::addOutputOp(cls, "__str__");
363  utils::python::addOutputOp(cls, "__repr__");
364  // The Key methods below actually wrap templated methods on Schema and
365  // SchemaMapper. Rather than doing many-type overload resolution by
366  // wrapping those methods directly, we use the visitor pattern by having
367  // the wrappers for those methods delegate back to these non-templated
368  // methods on the templated Key classes.
369  cls.def("_findIn", [](Key<T> const &self, Schema const &schema) { return schema.find(self); });
370  cls.def("_addMappingTo", [](Key<T> const &self, SchemaMapper &mapper, Field<T> const &field,
371  bool doReplace) { return mapper.addMapping(self, field, doReplace); });
372  cls.def("_addMappingTo", [](Key<T> const &self, SchemaMapper &mapper, std::string const &name,
373  bool doReplace) { return mapper.addMapping(self, name, doReplace); });
374  cls.def("_addMappingTo", [](Key<T> const &self, SchemaMapper &mapper, py::object const &,
375  bool doReplace) { return mapper.addMapping(self, doReplace); });
376  declareKeySpecializations(cls);
377  });
378 
379  // SchemaItem
380  wrappers.wrapType(PySchemaItem<T>(wrappers.module, ("SchemaItem" + suffix).c_str()),
381  [pySuffix](auto &mod, auto &cls) {
382  mod.attr("_SchemaItem")[pySuffix] = cls;
383  cls.def_readonly("key", &SchemaItem<T>::key);
384  cls.def_readonly("field", &SchemaItem<T>::field);
385  cls.def("getKey", [](SchemaItem<T> const &self) { return self.key; });
386  cls.def("getField", [](SchemaItem<T> const &self) { return self.field; });
387  cls.def("__getitem__", [](py::object const &self, int index) -> py::object {
388  if (index == 0) {
389  return self.attr("key");
390  } else if (index == 1) {
391  return self.attr("field");
392  }
393  // Have to raise IndexError not some LSST exception to get the
394  // right behavior when unpacking.
395  throw py::index_error("Index to SchemaItem must be 0 or 1.");
396  });
397  cls.def("__len__", [](py::object const &self) -> int { return 2; });
398  cls.def("__str__",
399  [](py::object const &self) -> py::str { return py::str(py::tuple(self)); });
400  cls.def("__repr__", [](py::object const &self) -> py::str {
401  return py::str("SchemaItem(key={0.key}, field={0.field})").format(self);
402  });
403  cls.def(py::pickle(
404  [](SchemaItem<T> const &self) {
405  /* Return a tuple that fully encodes the state of the object */
406  return py::make_tuple(self.key, self.field);
407  },
408  [](py::tuple t) {
409  int const NPARAMS = 2;
410  if (t.size() != NPARAMS) {
412  os << "Invalid number of parameters (" << t.size()
413  << ") when unpickling; expected " << NPARAMS;
414  throw std::runtime_error(os.str());
415  }
416  return SchemaItem<T>(t[0].cast<Key<T>>(), t[1].cast<Field<T>>());
417  }));
418  });
419 }
420 
421 // Helper class for Schema::find(name, func) that converts the result to Python.
422 // In C++14, this should be converted to a universal lambda.
423 struct MakePythonSchemaItem {
424  template <typename T>
425  void operator()(SchemaItem<T> const &item) {
426  result = py::cast(item);
427  }
428 
429  py::object result;
430 };
431 
432 void declareSchema(WrapperCollection &wrappers) {
433  wrappers.wrapType(PySchema(wrappers.module, "Schema"), [](auto &mod, auto &cls) {
434  // wrap ComparisonFlags values as ints since we use them as bitflags,
435  // not true enums
436  cls.attr("EQUAL_KEYS") = py::cast(int(Schema::EQUAL_KEYS));
437  cls.attr("EQUAL_NAMES") = py::cast(int(Schema::EQUAL_NAMES));
438  cls.attr("EQUAL_DOCS") = py::cast(int(Schema::EQUAL_DOCS));
439  cls.attr("EQUAL_UNITS") = py::cast(int(Schema::EQUAL_UNITS));
440  cls.attr("EQUAL_FIELDS") = py::cast(int(Schema::EQUAL_FIELDS));
441  cls.attr("EQUAL_ALIASES") = py::cast(int(Schema::EQUAL_ALIASES));
442  cls.attr("IDENTICAL") = py::cast(int(Schema::IDENTICAL));
443 
444  cls.attr("VERSION") = py::cast(int(Schema::VERSION));
445 
446  cls.def(py::init<>());
447  cls.def(py::init<Schema const &>());
448  cls.def("__getitem__", [](Schema &self, std::string const &name) { return self[name]; });
449  cls.def("__eq__", [](Schema const &self, Schema const &other) { return self == other; },
450  py::is_operator());
451  cls.def("__ne__", [](Schema const &self, Schema const &other) { return self != other; },
452  py::is_operator());
453  cls.def("getRecordSize", &Schema::getRecordSize);
454  cls.def("getFieldCount", &Schema::getFieldCount);
455  cls.def("getFlagFieldCount", &Schema::getFlagFieldCount);
456  cls.def("getNonFlagFieldCount", &Schema::getNonFlagFieldCount);
457  cls.def("find", [](py::object const &self, py::object const &key) -> py::object {
458  try {
459  if (py::isinstance<py::str>(key) || py::isinstance<py::bytes>(key)) {
460  Schema const &s = py::cast<Schema const &>(self);
461  std::string name = py::cast<std::string>(key);
462  MakePythonSchemaItem func;
463  s.findAndApply(name, func);
464  return func.result;
465  }
466  return key.attr("_findIn")(self);
467  } catch (pex::exceptions::NotFoundError &err) {
468  // Avoid API change by re-throwing as KeyError.
469  PyErr_SetString(PyExc_KeyError, err.what());
470  throw py::error_already_set();
471  }
472  });
473  cls.def("getNames", &Schema::getNames, "topOnly"_a = false);
474  cls.def("getAliasMap", &Schema::getAliasMap);
475  cls.def("setAliasMap", &Schema::setAliasMap, "aliases"_a);
476  cls.def("disconnectAliases", &Schema::disconnectAliases);
477  cls.def("forEach", [](Schema &self, py::object &obj) { self.forEach(obj); });
478  cls.def("compare", &Schema::compare, "other"_a, "flags"_a = int(Schema::EQUAL_KEYS));
479  cls.def("contains", (int (Schema::*)(Schema const &, int) const) & Schema::contains, "other"_a,
480  "flags"_a = int(Schema::EQUAL_KEYS));
481  cls.def("__contains__", [](py::object const &self, py::object const &key) {
482  try {
483  self.attr("find")(key);
484  } catch (py::error_already_set &err) {
485  err.restore();
486  PyErr_Clear();
487  return false;
488  }
489  return true;
490  });
491  cls.def_static("readFits", (Schema(*)(std::string const &, int)) & Schema::readFits, "filename"_a,
492  "hdu"_a = fits::DEFAULT_HDU);
493  cls.def_static("readFits", (Schema(*)(fits::MemFileManager &, int)) & Schema::readFits, "manager"_a,
494  "hdu"_a = fits::DEFAULT_HDU);
495 
496  cls.def("join",
497  (std::string(Schema::*)(std::string const &, std::string const &) const) & Schema::join,
498  "a"_a, "b"_a);
499  cls.def("join",
500  (std::string(Schema::*)(std::string const &, std::string const &, std::string const &)
501  const) &
502  Schema::join,
503  "a"_a, "b"_a, "c"_a);
504  cls.def("join",
505  (std::string(Schema::*)(std::string const &, std::string const &, std::string const &,
506  std::string const &) const) &
507  Schema::join,
508  "a"_a, "b"_a, "c"_a, "d"_a);
509  utils::python::addOutputOp(cls, "__str__");
510  utils::python::addOutputOp(cls, "__repr__");
511  });
512 }
513 
514 void declareSubSchema(WrapperCollection &wrappers) {
515  wrappers.wrapType(PySubSchema(wrappers.module, "SubSchema"), [](auto &mod, auto &cls) {
516  cls.def("getNames", &SubSchema::getNames, "topOnly"_a = false);
517  cls.def("getPrefix", &SubSchema::getPrefix);
518  cls.def("asKey", [](SubSchema const &self) -> py::object {
519  MakePythonSchemaItem func;
520  self.apply(func);
521  return func.result.attr("key");
522  });
523  cls.def("asField", [](SubSchema const &self) -> py::object {
524  MakePythonSchemaItem func;
525  self.apply(func);
526  return func.result.attr("field");
527  });
528  cls.def("find", [](SubSchema const &self, std::string const &name) -> py::object {
529  MakePythonSchemaItem func;
530  self.findAndApply(name, func);
531  return func.result;
532  });
533  cls.def("__getitem__", [](SubSchema &self, std::string const &name) { return self[name]; });
534  });
535 }
536 
537 } // namespace
538 
539 void wrapSchema(WrapperCollection &wrappers) {
540  // We'll add instantiations of Field, Key, and SchemaItem to these private
541  // dicts, and then in schemaContinued.py we'll add them to a TemplateMeta
542  // ABC.
543  auto &mod = wrappers.module;
544  mod.attr("_Field") = py::dict();
545  mod.attr("_Key") = py::dict();
546  mod.attr("_SchemaItem") = py::dict();
547 
548  declareSchemaType<std::uint8_t>(wrappers);
549  declareSchemaType<std::uint16_t>(wrappers);
550  declareSchemaType<std::int32_t>(wrappers);
551  declareSchemaType<std::int64_t>(wrappers);
552  declareSchemaType<float>(wrappers);
553  declareSchemaType<double>(wrappers);
554  declareSchemaType<std::string>(wrappers);
555  declareSchemaType<lsst::geom::Angle>(wrappers);
556  declareSchemaType<Array<std::uint8_t>>(wrappers);
557  declareSchemaType<Array<std::uint16_t>>(wrappers);
558  declareSchemaType<Array<int>>(wrappers);
559  declareSchemaType<Array<float>>(wrappers);
560  declareSchemaType<Array<double>>(wrappers);
561  declareSchemaType<Flag>(wrappers);
562 
563  declareSchema(wrappers);
564  declareSubSchema(wrappers);
565 }
566 
567 } // namespace table
568 } // namespace afw
569 } // namespace lsst
py::object result
Definition: _schema.cc:429
table::Key< std::string > name
Definition: Amplifier.cc:116
table::Key< int > field
Definition: ApCorrMap.cc:77
int const step
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition: Exception.h:48
std::ostream * os
Definition: Schema.cc:557
SchemaMapper * mapper
Definition: SchemaMapper.cc:71
table::Schema schema
Definition: python.h:134
daf::base::PropertyList * list
Definition: fits.cc:913
bool isValid
Definition: fits.cc:399
const int DEFAULT_HDU
Specify that the default HDU should be read.
Definition: fitsDefaults.h:18
std::string const & getName() const noexcept
Return a filter's name.
Definition: Filter.h:78
void wrapSchema(WrapperCollection &wrappers)
Definition: _schema.cc:539
constexpr Key< K, V > makeKey(K const &id)
Factory function for Key, to enable type parameter inference.
Definition: Key.h:173
void addHash(PyClass &cls)
Add __hash__ method implemented by std::hash.
Definition: python.h:104
void addOutputOp(PyClass &cls, std::string const &method)
Add __str__ or __repr__ method implemented by operator<<.
Definition: python.h:87
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:174
A base class for image defects.
T ref(T... args)