LSST Applications g1653933729+b86e4b8053,g2303c004a3+a99d6d42bd,g28da252d5a+3e0fd49a16,g2bbee38e9b+db5ba6d06c,g2bc492864f+db5ba6d06c,g2cdde0e794+b03b09fb0a,g3156d2b45e+b0ccf75d80,g347aa1857d+db5ba6d06c,g35bb328faa+b86e4b8053,g3a166c0a6a+db5ba6d06c,g3e281a1b8c+c9a36f98fc,g414038480c+70d5606f74,g42360bd172+a99d6d42bd,g461a3dce89+b86e4b8053,g5c9cb89fa5+b7dae1cdc6,g7f102674c4+d27c5e2769,g7f343241b9+516b07f5d7,g80478fca09+ee10125a54,g82479be7b0+11c35b2845,g858d7b2824+a99d6d42bd,g8cd86fa7b1+cb4c24add2,g9125e01d80+b86e4b8053,g979bb04a14+30ae7a28c8,g9ddcbc5298+b4cb6f9276,ga44b09e51c+390e0c76f2,ga5288a1d22+c32cd41bfc,gae0086650b+b86e4b8053,gb58c049af0+2fe46b4317,gc28159a63d+db5ba6d06c,gcd45df26be+a99d6d42bd,gcf0d15dbbd+be93eff7c7,gda6a2b7d83+be93eff7c7,gdaeeff99f8+b9455e0a82,ge5cf12406b+e20940066e,ge79ae78c31+db5ba6d06c,ge7c99ddb59+26aa1bc97f,gf048a9a2f4+f6d4943a12,gf0baf85859+e0282cf37d,w.2024.32
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