LSST Applications  21.0.0+04719a4bac,21.0.0-1-ga51b5d4+f5e6047307,21.0.0-11-g2b59f77+a9c1acf22d,21.0.0-11-ga42c5b2+86977b0b17,21.0.0-12-gf4ce030+76814010d2,21.0.0-13-g1721dae+760e7a6536,21.0.0-13-g3a573fe+768d78a30a,21.0.0-15-g5a7caf0+f21cbc5713,21.0.0-16-g0fb55c1+b60e2d390c,21.0.0-19-g4cded4ca+71a93a33c0,21.0.0-2-g103fe59+bb20972958,21.0.0-2-g45278ab+04719a4bac,21.0.0-2-g5242d73+3ad5d60fb1,21.0.0-2-g7f82c8f+8babb168e8,21.0.0-2-g8f08a60+06509c8b61,21.0.0-2-g8faa9b5+616205b9df,21.0.0-2-ga326454+8babb168e8,21.0.0-2-gde069b7+5e4aea9c2f,21.0.0-2-gecfae73+1d3a86e577,21.0.0-2-gfc62afb+3ad5d60fb1,21.0.0-25-g1d57be3cd+e73869a214,21.0.0-3-g357aad2+ed88757d29,21.0.0-3-g4a4ce7f+3ad5d60fb1,21.0.0-3-g4be5c26+3ad5d60fb1,21.0.0-3-g65f322c+e0b24896a3,21.0.0-3-g7d9da8d+616205b9df,21.0.0-3-ge02ed75+a9c1acf22d,21.0.0-4-g591bb35+a9c1acf22d,21.0.0-4-g65b4814+b60e2d390c,21.0.0-4-gccdca77+0de219a2bc,21.0.0-4-ge8a399c+6c55c39e83,21.0.0-5-gd00fb1e+05fce91b99,21.0.0-6-gc675373+3ad5d60fb1,21.0.0-64-g1122c245+4fb2b8f86e,21.0.0-7-g04766d7+cd19d05db2,21.0.0-7-gdf92d54+04719a4bac,21.0.0-8-g5674e7b+d1bd76f71f,master-gac4afde19b+a9c1acf22d,w.2021.13
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 (int 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 (int 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  cls.def_readonly_static("HAS_NAMED_SUBFIELDS", &KeyBase<T>::HAS_NAMED_SUBFIELDS);
318  declareKeyBaseSpecializations(cls);
319  });
320 
321  // Field
322  wrappers.wrapType(PyField<T>(wrappers.module, ("Field" + suffix).c_str()), [pySuffix, astropyUnit](
323  auto &mod, auto &cls) {
324  declareFieldSpecializations(cls);
325 
326  mod.attr("_Field")[pySuffix] = cls;
327 
328  cls.def(py::init([astropyUnit]( // capture by value to refcount in Python instead of dangle in C++
329  std::string const &name, std::string const &doc, py::str const &units,
330  py::object const &size, py::str const &parse_strict) {
331  astropyUnit(units, "parse_strict"_a = parse_strict);
332  std::string u = py::cast<std::string>(units);
333  if (size.is(py::none())) {
334  return new Field<T>(name, doc, u);
335  } else {
336  int s = py::cast<int>(size);
337  return new Field<T>(name, doc, u, s);
338  }
339  }),
340  "name"_a, "doc"_a = "", "units"_a = "", "size"_a = py::none(), "parse_strict"_a = "raise");
341  cls.def("_addTo", [](Field<T> const &self, Schema &schema, bool doReplace) -> Key<T> {
342  return schema.addField(self, doReplace);
343  });
344  cls.def("getName", &Field<T>::getName);
345  cls.def("getDoc", &Field<T>::getDoc);
346  cls.def("getUnits", &Field<T>::getUnits);
347  cls.def("copyRenamed", &Field<T>::copyRenamed);
348  utils::python::addOutputOp(cls, "__str__");
349  utils::python::addOutputOp(cls, "__repr__");
350  });
351 
352  // Key
353  wrappers.wrapType(PyKey<T>(wrappers.module, ("Key" + suffix).c_str()), [pySuffix](auto &mod, auto &cls) {
354  mod.attr("_Key")[pySuffix] = cls;
355  cls.def(py::init<>());
356  cls.def("__eq__", [](Key<T> const &self, Key<T> const &other) -> bool { return self == other; },
357  py::is_operator());
359  cls.def("__ne__", [](Key<T> const &self, Key<T> const &other) -> bool { return self != other; },
360  py::is_operator());
361  cls.def("isValid", &Key<T>::isValid);
362  cls.def("getOffset", &Key<T>::getOffset);
363  utils::python::addOutputOp(cls, "__str__");
364  utils::python::addOutputOp(cls, "__repr__");
365  // The Key methods below actually wrap templated methods on Schema and
366  // SchemaMapper. Rather than doing many-type overload resolution by
367  // wrapping those methods directly, we use the visitor pattern by having
368  // the wrappers for those methods delegate back to these non-templated
369  // methods on the templated Key classes.
370  cls.def("_findIn", [](Key<T> const &self, Schema const &schema) { return schema.find(self); });
371  cls.def("_addMappingTo", [](Key<T> const &self, SchemaMapper &mapper, Field<T> const &field,
372  bool doReplace) { return mapper.addMapping(self, field, doReplace); });
373  cls.def("_addMappingTo", [](Key<T> const &self, SchemaMapper &mapper, std::string const &name,
374  bool doReplace) { return mapper.addMapping(self, name, doReplace); });
375  cls.def("_addMappingTo", [](Key<T> const &self, SchemaMapper &mapper, py::object const &,
376  bool doReplace) { return mapper.addMapping(self, doReplace); });
377  declareKeySpecializations(cls);
378  });
379 
380  // SchemaItem
381  wrappers.wrapType(PySchemaItem<T>(wrappers.module, ("SchemaItem" + suffix).c_str()),
382  [pySuffix](auto &mod, auto &cls) {
383  mod.attr("_SchemaItem")[pySuffix] = cls;
384  cls.def_readonly("key", &SchemaItem<T>::key);
385  cls.def_readonly("field", &SchemaItem<T>::field);
386  cls.def("getKey", [](SchemaItem<T> const &self) { return self.key; });
387  cls.def("getField", [](SchemaItem<T> const &self) { return self.field; });
388  cls.def("__getitem__", [](py::object const &self, int index) -> py::object {
389  if (index == 0) {
390  return self.attr("key");
391  } else if (index == 1) {
392  return self.attr("field");
393  }
394  // Have to raise IndexError not some LSST exception to get the
395  // right behavior when unpacking.
396  throw py::index_error("Index to SchemaItem must be 0 or 1.");
397  });
398  cls.def("__len__", [](py::object const &self) -> int { return 2; });
399  cls.def("__str__",
400  [](py::object const &self) -> py::str { return py::str(py::tuple(self)); });
401  cls.def("__repr__", [](py::object const &self) -> py::str {
402  return py::str("SchemaItem(key={0.key}, field={0.field})").format(self);
403  });
404  cls.def(py::pickle(
405  [](SchemaItem<T> const &self) {
406  /* Return a tuple that fully encodes the state of the object */
407  return py::make_tuple(self.key, self.field);
408  },
409  [](py::tuple t) {
410  int const NPARAMS = 2;
411  if (t.size() != NPARAMS) {
413  os << "Invalid number of parameters (" << t.size()
414  << ") when unpickling; expected " << NPARAMS;
415  throw std::runtime_error(os.str());
416  }
417  return SchemaItem<T>(t[0].cast<Key<T>>(), t[1].cast<Field<T>>());
418  }));
419  });
420 }
421 
422 // Helper class for Schema::find(name, func) that converts the result to Python.
423 // In C++14, this should be converted to a universal lambda.
424 struct MakePythonSchemaItem {
425  template <typename T>
426  void operator()(SchemaItem<T> const &item) {
427  result = py::cast(item);
428  }
429 
430  py::object result;
431 };
432 
433 void declareSchema(WrapperCollection &wrappers) {
434  wrappers.wrapType(PySchema(wrappers.module, "Schema"), [](auto &mod, auto &cls) {
435  // wrap ComparisonFlags values as ints since we use them as bitflags,
436  // not true enums
437  cls.attr("EQUAL_KEYS") = py::cast(int(Schema::EQUAL_KEYS));
438  cls.attr("EQUAL_NAMES") = py::cast(int(Schema::EQUAL_NAMES));
439  cls.attr("EQUAL_DOCS") = py::cast(int(Schema::EQUAL_DOCS));
440  cls.attr("EQUAL_UNITS") = py::cast(int(Schema::EQUAL_UNITS));
441  cls.attr("EQUAL_FIELDS") = py::cast(int(Schema::EQUAL_FIELDS));
442  cls.attr("EQUAL_ALIASES") = py::cast(int(Schema::EQUAL_ALIASES));
443  cls.attr("IDENTICAL") = py::cast(int(Schema::IDENTICAL));
444 
445  cls.attr("VERSION") = py::cast(int(Schema::VERSION));
446 
447  cls.def(py::init<>());
448  cls.def(py::init<Schema const &>());
449  cls.def("__getitem__", [](Schema &self, std::string const &name) { return self[name]; });
450  cls.def("__eq__", [](Schema const &self, Schema const &other) { return self == other; },
451  py::is_operator());
452  cls.def("__ne__", [](Schema const &self, Schema const &other) { return self != other; },
453  py::is_operator());
454  cls.def("getRecordSize", &Schema::getRecordSize);
455  cls.def("getFieldCount", &Schema::getFieldCount);
456  cls.def("getFlagFieldCount", &Schema::getFlagFieldCount);
457  cls.def("getNonFlagFieldCount", &Schema::getNonFlagFieldCount);
458  cls.def("find", [](py::object const &self, py::object const &key) -> py::object {
459  try {
460  if (py::isinstance<py::str>(key) || py::isinstance<py::bytes>(key)) {
461  Schema const &s = py::cast<Schema const &>(self);
462  std::string name = py::cast<std::string>(key);
463  MakePythonSchemaItem func;
464  s.findAndApply(name, func);
465  return func.result;
466  }
467  return key.attr("_findIn")(self);
468  } catch (pex::exceptions::NotFoundError &err) {
469  // Avoid API change by re-throwing as KeyError.
470  PyErr_SetString(PyExc_KeyError, err.what());
471  throw py::error_already_set();
472  }
473  });
474  cls.def("getNames", &Schema::getNames, "topOnly"_a = false);
475  cls.def("getAliasMap", &Schema::getAliasMap);
476  cls.def("setAliasMap", &Schema::setAliasMap, "aliases"_a);
477  cls.def("disconnectAliases", &Schema::disconnectAliases);
478  cls.def("forEach", [](Schema &self, py::object &obj) { self.forEach(obj); });
479  cls.def("compare", &Schema::compare, "other"_a, "flags"_a = int(Schema::EQUAL_KEYS));
480  cls.def("contains", (int (Schema::*)(Schema const &, int) const) & Schema::contains, "other"_a,
481  "flags"_a = int(Schema::EQUAL_KEYS));
482  cls.def("__contains__", [](py::object const &self, py::object const &key) {
483  try {
484  self.attr("find")(key);
485  } catch (py::error_already_set &err) {
486  err.restore();
487  PyErr_Clear();
488  return false;
489  }
490  return true;
491  });
492  cls.def_static("readFits", (Schema(*)(std::string const &, int)) & Schema::readFits, "filename"_a,
493  "hdu"_a = fits::DEFAULT_HDU);
494  cls.def_static("readFits", (Schema(*)(fits::MemFileManager &, int)) & Schema::readFits, "manager"_a,
495  "hdu"_a = fits::DEFAULT_HDU);
496 
497  cls.def("join",
498  (std::string(Schema::*)(std::string const &, std::string const &) const) & Schema::join,
499  "a"_a, "b"_a);
500  cls.def("join",
501  (std::string(Schema::*)(std::string const &, std::string const &, std::string const &)
502  const) &
503  Schema::join,
504  "a"_a, "b"_a, "c"_a);
505  cls.def("join",
506  (std::string(Schema::*)(std::string const &, std::string const &, std::string const &,
507  std::string const &) const) &
508  Schema::join,
509  "a"_a, "b"_a, "c"_a, "d"_a);
510  utils::python::addOutputOp(cls, "__str__");
511  utils::python::addOutputOp(cls, "__repr__");
512  });
513 }
514 
515 void declareSubSchema(WrapperCollection &wrappers) {
516  wrappers.wrapType(PySubSchema(wrappers.module, "SubSchema"), [](auto &mod, auto &cls) {
517  cls.def("getNames", &SubSchema::getNames, "topOnly"_a = false);
518  cls.def("getPrefix", &SubSchema::getPrefix);
519  cls.def("asKey", [](SubSchema const &self) -> py::object {
520  MakePythonSchemaItem func;
521  self.apply(func);
522  return func.result.attr("key");
523  });
524  cls.def("asField", [](SubSchema const &self) -> py::object {
525  MakePythonSchemaItem func;
526  self.apply(func);
527  return func.result.attr("field");
528  });
529  cls.def("find", [](SubSchema const &self, std::string const &name) -> py::object {
530  MakePythonSchemaItem func;
531  self.findAndApply(name, func);
532  return func.result;
533  });
534  cls.def("__getitem__", [](SubSchema &self, std::string const &name) { return self[name]; });
535  });
536 }
537 
538 } // namespace
539 
540 void wrapSchema(WrapperCollection &wrappers) {
541  // We'll add instantiations of Field, Key, and SchemaItem to these private
542  // dicts, and then in schemaContinued.py we'll add them to a TemplateMeta
543  // ABC.
544  auto &mod = wrappers.module;
545  mod.attr("_Field") = py::dict();
546  mod.attr("_Key") = py::dict();
547  mod.attr("_SchemaItem") = py::dict();
548 
549  declareSchemaType<std::uint8_t>(wrappers);
550  declareSchemaType<std::uint16_t>(wrappers);
551  declareSchemaType<std::int32_t>(wrappers);
552  declareSchemaType<std::int64_t>(wrappers);
553  declareSchemaType<float>(wrappers);
554  declareSchemaType<double>(wrappers);
555  declareSchemaType<std::string>(wrappers);
556  declareSchemaType<lsst::geom::Angle>(wrappers);
557  declareSchemaType<Array<std::uint8_t>>(wrappers);
558  declareSchemaType<Array<std::uint16_t>>(wrappers);
559  declareSchemaType<Array<int>>(wrappers);
560  declareSchemaType<Array<float>>(wrappers);
561  declareSchemaType<Array<double>>(wrappers);
562  declareSchemaType<Flag>(wrappers);
563 
564  declareSchema(wrappers);
565  declareSubSchema(wrappers);
566 }
567 
568 } // namespace table
569 } // namespace afw
570 } // namespace lsst
py::object result
Definition: _schema.cc:430
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
ItemVariant const * other
Definition: Schema.cc:56
Key< U > key
Definition: Schema.cc:281
std::ostream * os
Definition: Schema.cc:746
SchemaMapper * mapper
Definition: SchemaMapper.cc:78
table::Schema schema
Definition: python.h:134
A helper class for subdividing pybind11 module across multiple translation units (i....
Definition: python.h:242
pybind11::module module
The module object passed to the PYBIND11_MODULE block that contains this WrapperCollection.
Definition: python.h:448
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:540
constexpr Key< K, V > makeKey(K const &id)
Factory function for Key, to enable type parameter inference.
Definition: Key.h:173
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:174
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
A base class for image defects.
T ref(T... args)