LSST Applications  21.0.0+3c14b91618,21.0.0+9f51b1e3f7,21.0.0-1-ga51b5d4+6691386486,21.0.0-10-g2408eff+1a328412bf,21.0.0-10-g560fb7b+52fd22d7b4,21.0.0-10-g8d1d15d+2f9043cae0,21.0.0-10-gcf60f90+d15de71c48,21.0.0-11-g25eff31+d43066e4ef,21.0.0-15-g490e301a+a676f0d5cf,21.0.0-2-g103fe59+4758c8ef83,21.0.0-2-g1367e85+a9f57e981a,21.0.0-2-g45278ab+9f51b1e3f7,21.0.0-2-g5242d73+a9f57e981a,21.0.0-2-g7f82c8f+1bcc828e4f,21.0.0-2-g8f08a60+e6fd6d9ff9,21.0.0-2-ga326454+1bcc828e4f,21.0.0-2-gde069b7+66c51b65da,21.0.0-2-gecfae73+251b9830c3,21.0.0-2-gfc62afb+a9f57e981a,21.0.0-20-g09baf175d+e1e7d1c708,21.0.0-3-g357aad2+854c3902c3,21.0.0-3-g4be5c26+a9f57e981a,21.0.0-3-g65f322c+feaa1990e9,21.0.0-3-g7d9da8d+3c14b91618,21.0.0-3-ge02ed75+bda8df9b93,21.0.0-4-g591bb35+bda8df9b93,21.0.0-4-g65b4814+52fd22d7b4,21.0.0-4-g88306b8+656365ce3f,21.0.0-4-gccdca77+86bf7a300d,21.0.0-4-ge8a399c+b99e86088e,21.0.0-5-gd00fb1e+6a0dc09319,21.0.0-51-gd3b42663+bf9f0d1c8b,21.0.0-6-g2d4f3f3+9f51b1e3f7,21.0.0-7-g04766d7+ee2ae02087,21.0.0-7-g98eecf7+3609eddee2,21.0.0-7-gde99fe0+bda8df9b93,21.0.0-8-gd70ce6f+79e45e76e4,master-gac4afde19b+bda8df9b93,w.2021.10
LSST Data Management Base Package
python.h
Go to the documentation of this file.
1 // -*- lsst-c++ -*-
2 /*
3  * LSST Data Management System
4  * See COPYRIGHT file at the top of the source tree.
5  *
6  * This product includes software developed by the
7  * LSST Project (http://www.lsst.org/).
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the LSST License Statement and
20  * the GNU General Public License along with this program. If not,
21  * see <http://www.lsstcorp.org/LegalNotices/>.
22  */
23 
24 #ifndef LSST_UTILS_PYTHON_H
25 #define LSST_UTILS_PYTHON_H
26 
27 #include "pybind11/pybind11.h"
28 
29 #include <cstddef>
30 #include <memory>
31 #include <string>
32 #include <sstream>
33 #include <utility>
34 #include <list>
35 #include <functional>
36 
37 #include <iostream>
38 
39 #include "lsst/pex/exceptions.h"
41 
42 namespace lsst {
43 namespace utils {
44 namespace python {
45 
62 template<typename T, typename PyClass>
63 inline void addSharedPtrEquality(PyClass & cls) {
64  cls.def("__eq__",
65  [](std::shared_ptr<T> self, std::shared_ptr<T> other) { return self.get() == other.get(); },
66  pybind11::is_operator());
67  cls.def("__ne__",
68  [](std::shared_ptr<T> self, std::shared_ptr<T> other) { return self.get() != other.get(); },
69  pybind11::is_operator());
70 }
71 
86 template <class PyClass>
87 void addOutputOp(PyClass &cls, std::string const &method) {
88  cls.def(method.c_str(), [](typename PyClass::type const &self) {
89  std::ostringstream os;
90  os << self;
91  return os.str();
92  });
93 }
94 
103 template <class PyClass>
104 void addHash(PyClass &cls) {
105  using Class = typename PyClass::type;
106  cls.def("__hash__", [](Class const &self) {
107  static auto const hash = std::hash<Class>();
108  return hash(self);
109  });
110 }
111 
125  auto const i_orig = i;
126  if (i < 0) {
127  // index backwards from the end
128  i += size;
129  }
130  if (i < 0 || i >= size) {
132  os << "Index " << i_orig << " not in range [" << -size << ", " << size - 1 << "]";
133  throw pybind11::index_error(os.str());
134  }
135  return static_cast<std::size_t>(i);
136 }
137 
152  try {
153  return {cppIndex(size_i, i), cppIndex(size_j, j)};
154  } catch (lsst::pex::exceptions::OutOfRangeError const&) {
156  os << "Index (" << i << ", " << j << ") not in range ["
157  << -size_i << ", " << size_i - 1 << "], ["
158  << -size_j << ", " << size_j - 1 << "]";
159  throw pybind11::index_error(os.str());
160  }
161 }
162 
243  // LSST_PRIVATE above: don't export symbols used only in pybind11 wrappers
244 public:
245 
248 
264  explicit WrapperCollection(pybind11::module module_, std::string const & package) :
265  module(module_),
266  _package(package)
267  {}
268 
269  // WrapperCollection is move-contructable.
271  module(std::move(other.module)),
272  _package(std::move(other._package)),
273  _dependencies(std::move(other._dependencies)),
274  _definitions(std::move(other._definitions))
275  {}
276 
277  // WrapperCollection is not copyable or assignable.
281 
282  ~WrapperCollection() noexcept {
283  if (!std::uncaught_exception() && !_definitions.empty()) {
284  PyErr_SetString(PyExc_ImportError,
285  "WrapperCollection::finish() not called; module definition incomplete.");
286  PyErr_WriteUnraisable(module.ptr());
287  }
288  }
289 
319  return WrapperCollection(module.def_submodule(("_" + name).c_str()), _package + "." + name);
320  }
321 
329  void collectSubmodule(WrapperCollection && submodule) {
330  _dependencies.splice(_dependencies.end(), submodule._dependencies);
331  _definitions.splice(_definitions.end(), submodule._definitions);
332  }
333 
344  pybind11::module::import(name.c_str());
345  }
346 
358  _dependencies.push_back(name);
359  }
360 
369  void wrap(WrapperCallback function) {
370  _definitions.emplace_back(std::make_pair(module, function));
371  }
372 
390  template <typename PyType, typename ClassWrapperCallback>
391  PyType wrapType(PyType cls, ClassWrapperCallback function, bool setModuleName=true) {
392  if (setModuleName) {
393  cls.attr("__module__") = _package;
394  }
395  // lambda below is mutable so it can modify the captured `cls` variable
396  wrap(
397  [cls=cls, function=std::move(function)] (pybind11::module & mod) mutable -> void {
398  function(mod, cls);
399  }
400  );
401  return cls;
402  }
403 
420  template <typename CxxException, typename CxxBase>
421  auto wrapException(std::string const & pyName, std::string const & pyBase, bool setModuleName=true) {
422  auto cls = pex::exceptions::python::declareException<CxxException, CxxBase>(module, pyName, pyBase);
423  if (setModuleName) {
424  cls.attr("__module__") = _package;
425  }
426  return cls;
427  }
428 
435  void finish() {
436  for (auto dep = _dependencies.begin(); dep != _dependencies.end(); dep = _dependencies.erase(dep)) {
437  pybind11::module::import(dep->c_str());
438  }
439  for (auto def = _definitions.begin(); def != _definitions.end(); def = _definitions.erase(def)) {
440  (def->second)(def->first); // WrapperCallback(module)
441  }
442  }
443 
449 
450 private:
451  std::string _package;
452  std::list<std::string> _dependencies;
454 };
455 
456 
457 }}} // namespace lsst::utils::python
458 
459 #endif
ItemVariant const * other
Definition: Schema.cc:56
std::ostream * os
Definition: Schema.cc:746
#define LSST_PRIVATE
Make a symbol hidden even if default visiblity is public.
Definition: base.h:66
T c_str(T... args)
Reports attempts to access elements outside a valid range of indices.
Definition: Runtime.h:89
A helper class for subdividing pybind11 module across multiple translation units (i....
Definition: python.h:242
WrapperCollection makeSubmodule(std::string const &name)
Create a WrapperCollection for a submodule defined in the same binary.
Definition: python.h:318
WrapperCollection & operator=(WrapperCollection &&)=delete
void collectSubmodule(WrapperCollection &&submodule)
Merge deferred definitions in the given submodule into the parent WrapperCollection.
Definition: python.h:329
auto wrapException(std::string const &pyName, std::string const &pyBase, bool setModuleName=true)
Wrap a C++ exception as a Python exception.
Definition: python.h:421
WrapperCollection(WrapperCollection const &)=delete
WrapperCollection(pybind11::module module_, std::string const &package)
Construct a new WrapperCollection.
Definition: python.h:264
WrapperCollection(WrapperCollection &&other) noexcept
Definition: python.h:270
WrapperCollection & operator=(WrapperCollection const &)=delete
pybind11::module module
The module object passed to the PYBIND11_MODULE block that contains this WrapperCollection.
Definition: python.h:448
void finish()
Invoke all deferred wrapper-declaring callables.
Definition: python.h:435
PyType wrapType(PyType cls, ClassWrapperCallback function, bool setModuleName=true)
Add a type (class or enum) wrapper, deferring method and other attribute definitions until finish() i...
Definition: python.h:391
void addInheritanceDependency(std::string const &name)
Indicate an external module that provides a base class for a subsequent addType call.
Definition: python.h:343
void wrap(WrapperCallback function)
Add a set of wrappers without defining a class.
Definition: python.h:369
void addSignatureDependency(std::string const &name)
Indicate an external module that provides a type used in function/method signatures.
Definition: python.h:357
T make_pair(T... args)
T move(T... args)
class[[deprecated("Removed with no replacement (but see lsst::afw::image::TransmissionCurve). Will be " "removed after v22.")]] FilterProperty final
Describe the properties of a Filter (e.g.
Definition: Filter.h:53
py::class_< PixelAreaBoundedField, std::shared_ptr< PixelAreaBoundedField >, BoundedField > PyClass
def wrap(ctrl)
Definition: wrap.py:302
std::size_t cppIndex(std::ptrdiff_t size, std::ptrdiff_t i)
Compute a C++ index from a Python index (negative values count from the end) and range-check.
Definition: python.h:124
void addSharedPtrEquality(PyClass &cls)
Add __eq__ and __ne__ methods based on two std::shared_ptr<T> pointing to the same address.
Definition: python.h:63
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.
table::Key< std::string > name
Definition: Amplifier.cc:116
table::Key< int > type
Definition: Detector.cc:163
T uncaught_exception(T... args)