LSST Applications g0afe43252f+b86e4b8053,g0e535e6de9+ad13c1b82d,g11f7dcd041+017865fdd3,g1ce3e0751c+f991eae79d,g25e82e299b+da9c58a417,g28da252d5a+9a4345bd89,g2bbee38e9b+b6588ad223,g2bc492864f+b6588ad223,g2cdde0e794+8523d0dbb4,g347aa1857d+b6588ad223,g35bb328faa+b86e4b8053,g3a166c0a6a+b6588ad223,g461a3dce89+b86e4b8053,g52b1c1532d+b86e4b8053,g7d11c3e888+3343bff751,g80478fca09+f22f659b46,g8543c18506+fadcf71ecf,g858d7b2824+c704b22fcc,g8cd86fa7b1+497903d09f,g965a9036f2+c704b22fcc,g979bb04a14+43c1c4070b,g9ddcbc5298+f24b38b85a,gae0086650b+b86e4b8053,gb781091699+0f9934ae6f,gbb886bcc26+486567bf06,gc28159a63d+b6588ad223,gc30aee3386+a2f0f6cab9,gc9f6898e11+66e322ebb3,gcaf7e4fdec+c704b22fcc,gcd45df26be+c704b22fcc,gcdd4ae20e8+02080e1d2d,gcf0d15dbbd+02080e1d2d,gdaeeff99f8+006e14e809,gdbce86181e+42405ade82,ge3d4d395c2+224150c836,ge79ae78c31+b6588ad223,gf048a9a2f4+69e41b07e6,gf0baf85859+b4cca3d10f,w.2024.30
LSST Data Management Base Package
Loading...
Searching...
No Matches
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
42namespace lsst {
43namespace cpputils {
44namespace python {
45
62template<typename T, typename PyClass>
63inline 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
86template <class PyClass>
87void 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
103template <class PyClass>
104void 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
242class LSST_PRIVATE WrapperCollection final {
243 // LSST_PRIVATE above: don't export symbols used only in pybind11 wrappers
244public:
245
247 using WrapperCallback = std::function<void(pybind11::module &)>;
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
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
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
448 pybind11::module module;
449
450private:
451 std::string _package;
452 std::list<std::string> _dependencies;
454};
455
456
457}}
458namespace utils = cpputils;
459} // namespace lsst::cpputils::python
460
461#endif
std::ostream * os
Definition Schema.cc:557
#define LSST_PRIVATE
Make a symbol hidden even if default visiblity is public.
Definition base.h:49
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 const &)=delete
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
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
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)
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