24 #include "pybind11/pybind11.h"
25 #include "pybind11/stl.h"
29 #include "ndarray/pybind11.h"
40 using namespace pybind11::literals;
46 using utils::python::WrapperCollection;
50 using PySchema = py::class_<Schema>;
52 using PySubSchema = py::class_<SubSchema>;
55 using PyFieldBase = py::class_<FieldBase<T>>;
58 using PyKeyBase = py::class_<KeyBase<T>>;
61 using PyField = py::class_<Field<T>, FieldBase<T>>;
64 using PyKey = py::class_<Key<T>, KeyBase<T>, FieldBase<T>>;
67 using PySchemaItem = py::class_<SchemaItem<T>>;
72 void declareFieldBaseSpecializations(PyFieldBase<T> &
cls) {
73 cls.def(py::init<>());
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);
83 void declareFieldBaseSpecializations(PyFieldBase<std::string> &
cls) {
84 cls.def(py::init<int>(),
"size"_a = -1);
91 void declareFieldSpecializations(PyField<T> &
cls) {
93 [](Field<T>
const &
self) {
95 return py::make_tuple(
self.getName(),
self.getDoc(),
self.getUnits());
98 int const NPARAMS = 3;
99 if (t.size() != NPARAMS) {
101 os <<
"Invalid number of parameters (" << t.size() <<
") when unpickling; expected "
105 return Field<T>(t[0].cast<std::string>(), t[1].cast<std::string>(), t[2].cast<std::string>());
110 template <
typename T>
111 void _sequenceFieldSpecializations(PyField<T> &
cls) {
113 [](Field<T>
const &
self) {
115 return py::make_tuple(
self.getName(),
self.getDoc(),
self.getUnits(),
self.
getSize());
118 int const NPARAMS = 4;
119 if (t.size() != NPARAMS) {
121 os <<
"Invalid number of parameters (" << t.size() <<
") when unpickling; expected "
125 return Field<T>(t[0].cast<std::string>(), t[1].cast<std::string>(), t[2].cast<std::string>(),
130 template <
typename T>
131 void declareFieldSpecializations(PyField<Array<T>> &
cls) {
132 _sequenceFieldSpecializations(
cls);
135 void declareFieldSpecializations(PyField<std::string> &
cls) { _sequenceFieldSpecializations(
cls); }
139 template <
typename T>
140 void declareKeyBaseSpecializations(PyKeyBase<T> &) {}
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;
149 if (!valid)
throw py::error_already_set();
151 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
152 "Step for array Key indexing must be 1.");
154 return py::cast(
self.slice(start, stop));
156 return py::cast(
self[py::cast<int>(index)]);
159 cls.def(
"slice", &KeyBase<Array<T>>::slice);
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);
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> {
177 auto setter = [](Key<Array<U>>
const &
self, BaseRecord &record, py::object
const &value) {
181 record.set(
self, py::cast<ndarray::Array<U, 1, 1>>(value));
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()) {
190 pex::exceptions::LengthError,
191 (
boost::format(
"Array sizes do not agree: %s != %s") % v.size() %
ref.size()).str());
197 cls.def(
"get", getter);
198 cls.def(
"set", setter);
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(); });
207 [](Key<T>
const &
self) {
209 return py::make_tuple(
self.getOffset());
212 int const NPARAMS = 1;
213 if (t.size() != NPARAMS) {
215 os <<
"Invalid number of parameters (" << t.size() <<
") when unpickling; expected "
219 return detail::Access::makeKey<T>(t[0].cast<int>());
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);
229 [](Key<Flag>
const &
self) {
231 return py::make_tuple(
self.getOffset(),
self.getBit());
234 int const NPARAMS = 2;
235 if (t.size() != NPARAMS) {
237 os <<
"Invalid number of parameters (" << t.size() <<
") when unpickling; expected "
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));
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]));
263 [](Key<Array<T>>
const &
self) {
265 return py::make_tuple(
self.getOffset(),
self.getElementCount());
268 int const NPARAMS = 2;
269 if (t.size() != NPARAMS) {
271 os <<
"Invalid number of parameters (" << t.size() <<
") when unpickling; expected "
275 return detail::Access::makeKeyArray<T>(t[0].cast<int>(), t[1].cast<int>());
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(); });
284 [](Key<std::string>
const &
self) {
286 return py::make_tuple(
self.getOffset(),
self.getElementCount());
289 int const NPARAMS = 2;
290 if (t.size() != NPARAMS) {
292 os <<
"Invalid number of parameters (" << t.size() <<
") when unpickling; expected "
296 return detail::Access::makeKeyString(t[0].cast<int>(), t[1].cast<int>());
301 template <
typename T>
302 void declareSchemaType(WrapperCollection &wrappers) {
303 std::string suffix = FieldBase<T>::getTypeString();
304 py::str pySuffix(suffix);
306 py::object astropyUnit = py::module::import(
"astropy.units").attr(
"Unit");
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);
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);
322 wrappers.wrapType(PyField<T>(wrappers.module, (
"Field" + suffix).c_str()), [pySuffix, astropyUnit](
323 auto &mod,
auto &
cls) {
324 declareFieldSpecializations(cls);
326 mod.attr(
"_Field")[pySuffix] = cls;
328 cls.def(py::init([astropyUnit](
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);
336 int s = py::cast<int>(size);
337 return new Field<T>(name, doc, u, s);
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);
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);
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; },
358 cls.def(
"__ne__", [](Key<T>
const &
self, Key<T>
const &
other) ->
bool {
return self !=
other; },
361 cls.def(
"getOffset", &Key<T>::getOffset);
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); });
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);
380 wrappers.wrapType(PySchemaItem<T>(wrappers.module, (
"SchemaItem" + suffix).c_str()),
381 [pySuffix](
auto &mod,
auto &
cls) {
382 mod.attr(
"_SchemaItem")[pySuffix] =
cls;
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 {
389 return self.attr(
"key");
390 }
else if (index == 1) {
391 return self.attr(
"field");
395 throw py::index_error(
"Index to SchemaItem must be 0 or 1.");
397 cls.def(
"__len__", [](py::object
const &
self) ->
int {
return 2; });
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);
404 [](SchemaItem<T>
const &
self) {
406 return py::make_tuple(
self.
key,
self.
field);
409 int const NPARAMS = 2;
410 if (t.size() != NPARAMS) {
412 os <<
"Invalid number of parameters (" << t.size()
413 <<
") when unpickling; expected " << NPARAMS;
416 return SchemaItem<T>(t[0].cast<Key<T>>(), t[1].cast<Field<T>>());
423 struct MakePythonSchemaItem {
424 template <
typename T>
425 void operator()(SchemaItem<T>
const &item) {
432 void declareSchema(WrapperCollection &wrappers) {
433 wrappers.wrapType(PySchema(wrappers.module,
"Schema"), [](
auto &mod,
auto &
cls) {
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));
444 cls.attr(
"VERSION") = py::cast(int(Schema::VERSION));
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; },
451 cls.def(
"__ne__", [](Schema
const &
self, Schema
const &
other) {
return self !=
other; },
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 {
459 if (py::isinstance<py::str>(
key) || py::isinstance<py::bytes>(
key)) {
460 Schema
const &s = py::cast<Schema const &>(
self);
462 MakePythonSchemaItem func;
463 s.findAndApply(
name, func);
466 return key.attr(
"_findIn")(
self);
467 }
catch (pex::exceptions::NotFoundError &err) {
469 PyErr_SetString(PyExc_KeyError, err.what());
470 throw py::error_already_set();
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) {
483 self.attr(
"find")(
key);
484 }
catch (py::error_already_set &err) {
491 cls.def_static(
"readFits", (Schema(*)(
std::string const &,
int)) & Schema::readFits,
"filename"_a,
493 cls.def_static(
"readFits", (Schema(*)(fits::MemFileManager &,
int)) & Schema::readFits,
"manager"_a,
503 "a"_a,
"b"_a,
"c"_a);
508 "a"_a,
"b"_a,
"c"_a,
"d"_a);
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;
521 return func.result.attr(
"key");
523 cls.def(
"asField", [](SubSchema
const &
self) -> py::object {
524 MakePythonSchemaItem func;
526 return func.result.attr(
"field");
529 MakePythonSchemaItem func;
530 self.findAndApply(
name, func);
543 auto &mod = wrappers.
module;
544 mod.attr(
"_Field") = py::dict();
545 mod.attr(
"_Key") = py::dict();
546 mod.attr(
"_SchemaItem") = py::dict();
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);
563 declareSchema(wrappers);
564 declareSubSchema(wrappers);