LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
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_CPPUTILS_PYTHON_H
25 #define LSST_CPPUTILS_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 cpputils {
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_exceptions()==0 && !_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 }}
458 namespace utils = cpputils;
459 } // namespace lsst::cpputils::python
460 
461 #endif
table::Key< std::string > name
Definition: Amplifier.cc:116
table::Key< int > type
Definition: Detector.cc:163
std::ostream * os
Definition: Schema.cc:557
#define LSST_PRIVATE
Make a symbol hidden even if default visiblity is public.
Definition: base.h:49
T c_str(T... args)
A helper class for subdividing pybind11 module across multiple translation units (i....
Definition: python.h:242
void addSignatureDependency(std::string const &name)
Indicate an external module that provides a type used in function/method signatures.
Definition: python.h:357
void wrap(WrapperCallback function)
Add a set of wrappers without defining a class.
Definition: python.h:369
WrapperCollection & operator=(WrapperCollection &&)=delete
void addInheritanceDependency(std::string const &name)
Indicate an external module that provides a base class for a subsequent addType call.
Definition: python.h:343
WrapperCollection makeSubmodule(std::string const &name)
Create a WrapperCollection for a submodule defined in the same binary.
Definition: python.h:318
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
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
WrapperCollection(WrapperCollection const &)=delete
WrapperCollection(WrapperCollection &&other) noexcept
Definition: python.h:270
WrapperCollection(pybind11::module module_, std::string const &package)
Construct a new WrapperCollection.
Definition: python.h:264
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
void collectSubmodule(WrapperCollection &&submodule)
Merge deferred definitions in the given submodule into the parent WrapperCollection.
Definition: python.h:329
WrapperCollection & operator=(WrapperCollection const &)=delete
Reports attempts to access elements outside a valid range of indices.
Definition: Runtime.h:89
T get(T... args)
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
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
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
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
def wrap(ctrl)
Definition: wrap.py:302
A base class for image defects.