LSSTApplications  16.0-10-g0ee56ad+4,16.0-11-ga33d1f2+4,16.0-12-g3ef5c14+2,16.0-12-g71e5ef5+17,16.0-12-gbdf3636+2,16.0-13-g118c103+2,16.0-13-g8f68b0a+2,16.0-15-gbf5c1cb+3,16.0-16-gfd17674+2,16.0-17-g7c01f5c+2,16.0-18-g0a50484,16.0-20-ga20f992+7,16.0-21-g0e05fd4+5,16.0-21-g15e2d33+3,16.0-22-g62d8060+3,16.0-22-g847a80f+3,16.0-25-gf00d9b8,16.0-28-g3990c221+3,16.0-3-gf928089+2,16.0-32-g88a4f23+4,16.0-34-gd7987ad+2,16.0-37-gc7333cb+1,16.0-4-g10fc685+1,16.0-4-g18f3627+25,16.0-4-g5f3a788+25,16.0-5-gaf5c3d7+3,16.0-5-gcc1f4bb,16.0-6-g3b92700+3,16.0-6-g4412fcd+2,16.0-6-g7235603+3,16.0-69-g2562ce1b+1,16.0-7-g0913a87,16.0-8-g14ebd58+3,16.0-8-g2df868b,16.0-8-g4cec79c+5,16.0-8-gadf6c7a,16.0-82-g59ec2a54a,16.0-9-g5400cdc+1,16.0-9-ge6233d7+4,master-g2880f2d8cf+2,v17.0.rc1
LSSTDataManagementBasePackage
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) {
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)};
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.
278  WrapperCollection(WrapperCollection const &) = delete;
279  WrapperCollection & operator=(WrapperCollection const &) = delete;
280  WrapperCollection & operator=(WrapperCollection &&) = delete;
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
WrapperCollection(pybind11::module module_, std::string const &package)
Construct a new WrapperCollection.
Definition: python.h:264
void addOutputOp(PyClass &cls, std::string const &method)
Add __str__ or __repr__ method implemented by operator<<.
Definition: python.h:87
WrapperCollection makeSubmodule(std::string const &name)
Create a WrapperCollection for a submodule defined in the same binary.
Definition: python.h:318
#define LSST_PRIVATE
Make a symbol hidden even if default visiblity is public.
Definition: base.h:66
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 addInheritanceDependency(std::string const &name)
Indicate an external module that provides a base class for a subsequent addType call.
Definition: python.h:343
void finish()
Invoke all deferred wrapper-declaring callables.
Definition: python.h:435
void wrap(WrapperCallback function)
Add a set of wrappers without defining a class.
Definition: python.h:369
def wrap(ctrl)
Definition: wrap.py:285
pybind11::module module
The module object passed to the PYBIND11_MODULE block that contains this WrapperCollection.
Definition: python.h:448
STL class.
void addSignatureDependency(std::string const &name)
Indicate an external module that provides a type used in function/method signatures.
Definition: python.h:357
A base class for image defects.
table::Key< int > type
Definition: Detector.cc:164
T str(T... args)
T make_pair(T... args)
T move(T... args)
T uncaught_exception(T... args)
void addHash(PyClass &cls)
Add __hash__ method implemented by std::hash.
Definition: python.h:104
T get(T... args)
Reports attempts to access elements outside a valid range of indices.
Definition: Runtime.h:89
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
T c_str(T... args)
ItemVariant const * other
Definition: Schema.cc:56
void collectSubmodule(WrapperCollection &&submodule)
Merge deferred definitions in the given submodule into the parent WrapperCollection.
Definition: python.h:329
A helper class for subdividing pybind11 module across multiple translation units (i.e.
Definition: python.h:242
WrapperCollection(WrapperCollection &&other) noexcept
Definition: python.h:270
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
std::ostream * os
Definition: Schema.cc:746
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