23 #include "pybind11/pybind11.h" 24 #include "pybind11/stl.h" 28 #include "ndarray/pybind11.h" 47 using PySchema = py::class_<Schema>;
49 using PySubSchema = py::class_<SubSchema>;
52 using PyFieldBase = py::class_<FieldBase<T>>;
55 using PyKeyBase = py::class_<KeyBase<T>>;
58 using PyField = py::class_<Field<T>, FieldBase<T>>;
61 using PyKey = py::class_<Key<T>, KeyBase<T>, FieldBase<T>>;
64 using PySchemaItem = py::class_<SchemaItem<T>>;
69 void declareFieldBaseSpecializations(PyFieldBase<T> &
cls) {
70 cls.def(py::init<>());
74 void declareFieldBaseSpecializations(PyFieldBase<Array<T>> &cls) {
75 cls.def(py::init<int>(),
"size"_a = 0);
76 cls.def(
"getSize", &FieldBase<Array<T>>::
getSize);
77 cls.def(
"isVariableLength", &FieldBase<Array<T>>::isVariableLength);
80 void declareFieldBaseSpecializations(PyFieldBase<std::string> &cls) {
81 cls.def(py::init<int>(),
"size"_a = -1);
88 void declareFieldSpecializations(PyField<T> &cls) {
90 [](Field<T>
const &
self) {
92 return py::make_tuple(
self.getName(),
self.getDoc(),
self.getUnits());
95 int const NPARAMS = 3;
96 if (t.size() != NPARAMS) {
98 os <<
"Invalid number of parameters (" << t.size() <<
") when unpickling; expected " 107 template <
typename T>
108 void _sequenceFieldSpecializations(PyField<T> &cls) {
110 [](Field<T>
const &
self) {
112 return py::make_tuple(
self.getName(),
self.getDoc(),
self.getUnits(),
self.
getSize());
115 int const NPARAMS = 4;
116 if (t.size() != NPARAMS) {
118 os <<
"Invalid number of parameters (" << t.size() <<
") when unpickling; expected " 127 template <
typename T>
128 void declareFieldSpecializations(PyField<Array<T>> &cls) {
129 _sequenceFieldSpecializations(cls);
132 void declareFieldSpecializations(PyField<std::string> &cls) { _sequenceFieldSpecializations(cls); }
136 template <
typename T>
137 void declareKeyBaseSpecializations(PyKeyBase<T> &) {}
139 template <
typename T>
140 void declareKeyBaseSpecializations(PyKeyBase<Array<T>> &cls) {
141 cls.def(
"__getitem__", [](Key<Array<T>>
const &
self, py::object
const &index) -> py::object {
142 if (py::isinstance<py::slice>(index)) {
143 py::slice slice(index);
144 py::size_t start = 0, stop = 0,
step = 0, length = 0;
145 bool valid = slice.compute(
self.
getSize(), &start, &stop, &
step, &length);
146 if (!valid)
throw py::error_already_set();
148 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
149 "Step for array Key indexing must be 1.");
151 return py::cast(
self.slice(start, stop));
153 return py::cast(
self[py::cast<int>(index)]);
156 cls.def(
"slice", &KeyBase<Array<T>>::slice);
161 template <
typename T>
162 void declareKeyAccessors(PyKey<T> &cls) {
163 cls.def(
"get", [](Key<T>
const &
self, BaseRecord &record) {
return record.get(
self); });
164 cls.def(
"set", [](Key<T>
const &
self, BaseRecord &record,
typename Key<T>::Value
const &value) {
165 record.set(
self, value);
169 template <
typename U>
170 void declareKeyAccessors(PyKey<Array<U>> &cls) {
171 auto getter = [](Key<Array<U>>
const &
self, BaseRecord &record) -> ndarray::Array<U, 1, 1> {
174 auto setter = [](Key<Array<U>>
const &
self, BaseRecord &record, py::object
const &value) {
178 record.set(
self, py::cast<ndarray::Array<U, 1, 1>>(value));
183 auto v = py::cast<ndarray::Array<U const, 1, 0>>(value);
184 ndarray::ArrayRef<U, 1, 1>
ref = record[
self];
185 if (v.size() != ref.size()) {
187 pex::exceptions::LengthError,
188 (
boost::format(
"Array sizes do not agree: %s != %s") % v.size() % ref.size()).
str());
194 cls.def(
"get", getter);
195 cls.def(
"set", setter);
198 template <
typename T>
199 void declareKeySpecializations(PyKey<T> &cls) {
200 declareKeyAccessors(cls);
201 cls.def_property_readonly(
"subfields", [](py::object
const &) {
return py::none(); });
202 cls.def_property_readonly(
"subkeys", [](py::object
const &) {
return py::none(); });
204 [](Key<T>
const &
self) {
206 return py::make_tuple(
self.getOffset());
209 int const NPARAMS = 1;
210 if (t.size() != NPARAMS) {
212 os <<
"Invalid number of parameters (" << t.size() <<
") when unpickling; expected " 216 return detail::Access::makeKey<T>(t[0].cast<
int>());
220 void declareKeySpecializations(PyKey<Flag> &cls) {
221 declareKeyAccessors(cls);
222 cls.def_property_readonly(
"subfields", [](py::object
const &) {
return py::none(); });
223 cls.def_property_readonly(
"subkeys", [](py::object
const &) {
return py::none(); });
224 cls.def(
"getBit", &Key<Flag>::getBit);
226 [](Key<Flag>
const &
self) {
228 return py::make_tuple(
self.getOffset(),
self.getBit());
231 int const NPARAMS = 2;
232 if (t.size() != NPARAMS) {
234 os <<
"Invalid number of parameters (" << t.size() <<
") when unpickling; expected " 242 template <
typename T>
243 void declareKeySpecializations(PyKey<Array<T>> &cls) {
244 declareKeyAccessors(cls);
245 cls.def_property_readonly(
"subfields", [](Key<Array<T>>
const &
self) -> py::object {
247 for (
int i = 0; i <
self.getSize(); ++i) {
248 result.append(py::cast(i));
250 return py::tuple(result);
252 cls.def_property_readonly(
"subkeys", [](Key<Array<T>>
const &
self) -> py::object {
254 for (
int i = 0; i <
self.getSize(); ++i) {
255 result.append(py::cast(
self[i]));
257 return py::tuple(result);
260 [](Key<Array<T>>
const &
self) {
262 return py::make_tuple(
self.getOffset(),
self.getElementCount());
265 int const NPARAMS = 2;
266 if (t.size() != NPARAMS) {
268 os <<
"Invalid number of parameters (" << t.size() <<
") when unpickling; expected " 272 return detail::Access::makeKeyArray<T>(t[0].cast<
int>(), t[1].cast<int>());
276 void declareKeySpecializations(PyKey<std::string> &cls) {
277 declareKeyAccessors(cls);
278 cls.def_property_readonly(
"subfields", [](py::object
const &) {
return py::none(); });
279 cls.def_property_readonly(
"subkeys", [](py::object
const &) {
return py::none(); });
281 [](Key<std::string>
const &
self) {
283 return py::make_tuple(
self.getOffset(),
self.getElementCount());
286 int const NPARAMS = 2;
287 if (t.size() != NPARAMS) {
289 os <<
"Invalid number of parameters (" << t.size() <<
") when unpickling; expected " 293 return detail::Access::makeKeyString(t[0].cast<int>(), t[1].cast<int>());
298 template <
typename T>
300 std::string suffix = FieldBase<T>::getTypeString();
303 py::object astropyUnit = py::module::import(
"astropy.units").attr(
"Unit");
306 PyFieldBase<T> clsFieldBase(mod, (
"FieldBase" + suffix).c_str());
307 clsFieldBase.def_static(
"getTypeString", &FieldBase<T>::getTypeString);
308 declareFieldBaseSpecializations(clsFieldBase);
311 PyKeyBase<T> clsKeyBase(mod, (
"KeyBase" + suffix).c_str());
312 clsKeyBase.def_readonly_static(
"HAS_NAMED_SUBFIELDS", &KeyBase<T>::HAS_NAMED_SUBFIELDS);
313 declareKeyBaseSpecializations(clsKeyBase);
316 PyField<T> clsField(mod, (
"Field" + suffix).c_str());
317 declareFieldSpecializations(clsField);
319 mod.attr(
"_Field")[pySuffix] = clsField;
320 clsField.def(
py::init([astropyUnit](
322 py::object
const &size,
py::str const &parse_strict) {
323 astropyUnit(units,
"parse_strict"_a = parse_strict);
325 if (size.is(py::none())) {
326 return new Field<T>(
name, doc, u);
328 int s = py::cast<int>(size);
329 return new Field<T>(
name, doc, u,
s);
332 "name"_a,
"doc"_a =
"",
"units"_a =
"",
"size"_a = py::none(),
"parse_strict"_a =
"raise");
333 clsField.def(
"_addTo", [](Field<T>
const &
self, Schema &
schema,
bool doReplace) -> Key<T> {
334 return schema.addField(
self, doReplace);
336 clsField.def(
"getName", &Field<T>::getName);
337 clsField.def(
"getDoc", &Field<T>::getDoc);
338 clsField.def(
"getUnits", &Field<T>::getUnits);
339 clsField.def(
"copyRenamed", &Field<T>::copyRenamed);
344 PyKey<T> clsKey(mod, (
"Key" + suffix).c_str());
345 mod.attr(
"_Key")[pySuffix] = clsKey;
346 clsKey.def(py::init<>());
348 "__eq__", [](Key<T>
const &
self, Key<T>
const &
other) ->
bool {
return self ==
other; },
351 "__ne__", [](Key<T>
const &
self, Key<T>
const &other) ->
bool {
return self !=
other; },
354 clsKey.def(
"getOffset", &Key<T>::getOffset);
362 clsKey.def(
"_findIn", [](Key<T>
const &
self, Schema
const &schema) {
return schema.find(
self); });
363 clsKey.def(
"_addMappingTo", [](Key<T>
const &
self, SchemaMapper &
mapper, Field<T>
const &
field,
364 bool doReplace) {
return mapper.addMapping(
self, field, doReplace); });
365 clsKey.def(
"_addMappingTo", [](Key<T>
const &
self, SchemaMapper &mapper,
std::string const &name,
366 bool doReplace) {
return mapper.addMapping(
self, name, doReplace); });
367 clsKey.def(
"_addMappingTo", [](Key<T>
const &
self, SchemaMapper &mapper, py::object
const &,
368 bool doReplace) {
return mapper.addMapping(
self, doReplace); });
369 declareKeySpecializations(clsKey);
372 PySchemaItem<T> clsSchemaItem(mod, (
"SchemaItem" + suffix).c_str());
373 mod.attr(
"_SchemaItem")[pySuffix] = clsSchemaItem;
376 clsSchemaItem.def(
"getKey", [](SchemaItem<T>
const &
self) {
return self.key; });
377 clsSchemaItem.def(
"getField", [](SchemaItem<T>
const &
self) {
return self.field; });
378 clsSchemaItem.def(
"__getitem__", [](py::object
const &
self,
int index) -> py::object {
380 return self.attr(
"key");
381 }
else if (index == 1) {
382 return self.attr(
"field");
386 throw py::index_error(
"Index to SchemaItem must be 0 or 1.");
388 clsSchemaItem.def(
"__len__", [](py::object
const &
self) ->
int {
return 2; });
389 clsSchemaItem.def(
"__str__", [](py::object
const &
self) ->
py::str {
return py::str(py::tuple(
self)); });
390 clsSchemaItem.def(
"__repr__", [](py::object
const &
self) ->
py::str {
391 return py::str(
"SchemaItem(key={0.key}, field={0.field})").format(
self);
393 clsSchemaItem.def(py::pickle(
394 [](SchemaItem<T>
const &
self) {
396 return py::make_tuple(
self.
key,
self.field);
399 int const NPARAMS = 2;
400 if (t.size() != NPARAMS) {
402 os <<
"Invalid number of parameters (" << t.size() <<
") when unpickling; expected " 406 return SchemaItem<T>(t[0].cast<Key<T>>(), t[1].cast<Field<T>>());
412 struct MakePythonSchemaItem {
413 template <
typename T>
414 void operator()(SchemaItem<T>
const &item) {
415 result = py::cast(item);
422 py::module::import(
"lsst.afw.table.aliasMap");
424 PySchema
cls(mod,
"Schema");
427 cls.attr(
"EQUAL_KEYS") = py::cast(
int(Schema::EQUAL_KEYS));
428 cls.attr(
"EQUAL_NAMES") = py::cast(
int(Schema::EQUAL_NAMES));
429 cls.attr(
"EQUAL_DOCS") = py::cast(
int(Schema::EQUAL_DOCS));
430 cls.attr(
"EQUAL_UNITS") = py::cast(
int(Schema::EQUAL_UNITS));
431 cls.attr(
"EQUAL_FIELDS") = py::cast(
int(Schema::EQUAL_FIELDS));
432 cls.attr(
"EQUAL_ALIASES") = py::cast(
int(Schema::EQUAL_ALIASES));
433 cls.attr(
"IDENTICAL") = py::cast(
int(Schema::IDENTICAL));
435 cls.attr(
"VERSION") = py::cast(
int(Schema::VERSION));
437 cls.def(py::init<>());
438 cls.def(py::init<Schema const &>());
439 cls.def(
"__getitem__", [](Schema &
self,
std::string const &name) {
return self[
name]; });
441 "__eq__", [](Schema
const &
self, Schema
const &other) {
return self ==
other; },
444 "__ne__", [](Schema
const &
self, Schema
const &other) {
return self !=
other; },
446 cls.def(
"getCitizen", &Schema::getCitizen, py::return_value_policy::reference_internal);
447 cls.def(
"getRecordSize", &Schema::getRecordSize);
448 cls.def(
"getFieldCount", &Schema::getFieldCount);
449 cls.def(
"getFlagFieldCount", &Schema::getFlagFieldCount);
450 cls.def(
"getNonFlagFieldCount", &Schema::getNonFlagFieldCount);
451 cls.def(
"find", [](py::object
const &
self, py::object
const &
key) -> py::object {
453 if (py::isinstance<py::str>(key) || py::isinstance<py::bytes>(key)) {
454 Schema
const &
s = py::cast<Schema const &>(
self);
456 MakePythonSchemaItem func;
457 s.findAndApply(name, func);
460 return key.attr(
"_findIn")(
self);
461 }
catch (pex::exceptions::NotFoundError &err) {
463 PyErr_SetString(PyExc_KeyError, err.what());
464 throw py::error_already_set();
467 cls.def(
"getNames", &Schema::getNames,
"topOnly"_a =
false);
468 cls.def(
"getAliasMap", &Schema::getAliasMap);
469 cls.def(
"setAliasMap", &Schema::setAliasMap,
"aliases"_a);
470 cls.def(
"disconnectAliases", &Schema::disconnectAliases);
471 cls.def(
"forEach", [](Schema &
self, py::object &obj) {
self.forEach(obj); });
472 cls.def(
"compare", &Schema::compare,
"other"_a,
"flags"_a =
int(Schema::EQUAL_KEYS));
473 cls.def(
"contains", (
int (Schema::*)(Schema
const &,
int)
const) &
Schema::contains,
"other"_a,
474 "flags"_a =
int(Schema::EQUAL_KEYS));
475 cls.def(
"__contains__", [](py::object
const &
self, py::object
const &key) {
477 self.attr(
"find")(
key);
478 }
catch (py::error_already_set &err) {
485 cls.def_static(
"readFits", (Schema(*)(
std::string const &,
int)) & Schema::readFits,
"filename"_a,
487 cls.def_static(
"readFits", (Schema(*)(fits::MemFileManager &,
int)) & Schema::readFits,
"manager"_a,
495 "a"_a,
"b"_a,
"c"_a);
500 "a"_a,
"b"_a,
"c"_a,
"d"_a);
506 PySubSchema
cls(mod,
"SubSchema");
507 cls.def(
"getNames", &SubSchema::getNames,
"topOnly"_a =
false);
508 cls.def(
"getPrefix", &SubSchema::getPrefix);
509 cls.def(
"asKey", [](SubSchema
const &
self) -> py::object {
510 MakePythonSchemaItem func;
512 return func.result.attr(
"key");
514 cls.def(
"asField", [](SubSchema
const &
self) -> py::object {
515 MakePythonSchemaItem func;
517 return func.result.attr(
"field");
519 cls.def(
"find", [](SubSchema
const &
self,
std::string const &name) -> py::object {
520 MakePythonSchemaItem func;
521 self.findAndApply(name, func);
524 cls.def(
"__getitem__", [](SubSchema &
self,
std::string const &name) {
return self[
name]; });
531 mod.attr(
"_Field") = py::dict();
532 mod.attr(
"_Key") = py::dict();
533 mod.attr(
"_SchemaItem") = py::dict();
535 declareSchemaType<std::uint8_t>(mod);
536 declareSchemaType<std::uint16_t>(mod);
537 declareSchemaType<std::int32_t>(mod);
538 declareSchemaType<std::int64_t>(mod);
539 declareSchemaType<float>(mod);
540 declareSchemaType<double>(mod);
541 declareSchemaType<std::string>(mod);
542 declareSchemaType<lsst::geom::Angle>(mod);
543 declareSchemaType<Array<std::uint8_t>>(mod);
544 declareSchemaType<Array<std::uint16_t>>(mod);
545 declareSchemaType<Array<int>>(mod);
546 declareSchemaType<Array<float>>(mod);
547 declareSchemaType<Array<double>>(mod);
548 declareSchemaType<Flag>(mod);
551 declareSubSchema(mod);
void addOutputOp(PyClass &cls, std::string const &method)
Add __str__ or __repr__ method implemented by operator<<.
bool contains(VertexIterator const begin, VertexIterator const end, UnitVector3d const &v)
PYBIND11_MODULE(camera, mod)
constexpr Key< K, V > makeKey(K const &id)
Factory function for Key, to enable type parameter inference.
A base class for image defects.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
ItemVariant const * other
daf::base::PropertyList * list
const int DEFAULT_HDU
Specify that the default HDU should be read.