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);
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; },
359 cls.def(
"__ne__", [](Key<T>
const &
self, Key<T>
const &
other) ->
bool {
return self !=
other; },
362 cls.def(
"getOffset", &Key<T>::getOffset);
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); });
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);
381 wrappers.wrapType(PySchemaItem<T>(wrappers.module, (
"SchemaItem" + suffix).c_str()),
382 [pySuffix](
auto &mod,
auto &cls) {
383 mod.attr(
"_SchemaItem")[pySuffix] = cls;
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 {
390 return self.attr(
"key");
391 }
else if (index == 1) {
392 return self.attr(
"field");
396 throw py::index_error(
"Index to SchemaItem must be 0 or 1.");
398 cls.def(
"__len__", [](py::object
const &
self) ->
int {
return 2; });
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);
405 [](SchemaItem<T>
const &
self) {
407 return py::make_tuple(
self.
key,
self.
field);
410 int const NPARAMS = 2;
411 if (t.size() != NPARAMS) {
413 os <<
"Invalid number of parameters (" << t.size()
414 <<
") when unpickling; expected " << NPARAMS;
417 return SchemaItem<T>(t[0].cast<Key<T>>(), t[1].cast<Field<T>>());
424 struct MakePythonSchemaItem {
425 template <
typename T>
426 void operator()(SchemaItem<T>
const &item) {
433 void declareSchema(WrapperCollection &wrappers) {
434 wrappers.wrapType(PySchema(wrappers.module,
"Schema"), [](
auto &mod,
auto &cls) {
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));
445 cls.attr(
"VERSION") = py::cast(int(Schema::VERSION));
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; },
452 cls.def(
"__ne__", [](Schema
const &
self, Schema
const &
other) {
return self !=
other; },
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 {
460 if (py::isinstance<py::str>(
key) || py::isinstance<py::bytes>(
key)) {
461 Schema
const &s = py::cast<Schema const &>(
self);
463 MakePythonSchemaItem func;
464 s.findAndApply(
name, func);
467 return key.attr(
"_findIn")(
self);
468 }
catch (pex::exceptions::NotFoundError &err) {
470 PyErr_SetString(PyExc_KeyError, err.what());
471 throw py::error_already_set();
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) {
484 self.attr(
"find")(
key);
485 }
catch (py::error_already_set &err) {
492 cls.def_static(
"readFits", (Schema(*)(
std::string const &,
int)) & Schema::readFits,
"filename"_a,
494 cls.def_static(
"readFits", (Schema(*)(fits::MemFileManager &,
int)) & Schema::readFits,
"manager"_a,
504 "a"_a,
"b"_a,
"c"_a);
509 "a"_a,
"b"_a,
"c"_a,
"d"_a);
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;
522 return func.result.attr(
"key");
524 cls.def(
"asField", [](SubSchema
const &
self) -> py::object {
525 MakePythonSchemaItem func;
527 return func.result.attr(
"field");
529 cls.def(
"find", [](SubSchema
const &
self,
std::string const &
name) -> py::object {
530 MakePythonSchemaItem func;
531 self.findAndApply(
name, func);
534 cls.def(
"__getitem__", [](SubSchema &
self,
std::string const &
name) {
return self[
name]; });
544 auto &mod = wrappers.
module;
545 mod.attr(
"_Field") = py::dict();
546 mod.attr(
"_Key") = py::dict();
547 mod.attr(
"_SchemaItem") = py::dict();
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);
564 declareSchema(wrappers);
565 declareSubSchema(wrappers);
table::Key< std::string > name
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
ItemVariant const * other
A helper class for subdividing pybind11 module across multiple translation units (i....
pybind11::module module
The module object passed to the PYBIND11_MODULE block that contains this WrapperCollection.
daf::base::PropertyList * list
const int DEFAULT_HDU
Specify that the default HDU should be read.
std::string const & getName() const noexcept
Return a filter's name.
void wrapSchema(WrapperCollection &wrappers)
constexpr Key< K, V > makeKey(K const &id)
Factory function for Key, to enable type parameter inference.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
void addHash(PyClass &cls)
Add __hash__ method implemented by std::hash.
void addOutputOp(PyClass &cls, std::string const &method)
Add __str__ or __repr__ method implemented by operator<<.
A base class for image defects.