LSST Applications g042eb84c57+730a74494b,g04e9c324dd+8c5ae1fdc5,g134cb467dc+1f1e3e7524,g199a45376c+0ba108daf9,g1fd858c14a+fa7d31856b,g210f2d0738+f66ac109ec,g262e1987ae+83a3acc0e5,g29ae962dfc+d856a2cb1f,g2cef7863aa+aef1011c0b,g35bb328faa+8c5ae1fdc5,g3fd5ace14f+a1e0c9f713,g47891489e3+0d594cb711,g4d44eb3520+c57ec8f3ed,g4d7b6aa1c5+f66ac109ec,g53246c7159+8c5ae1fdc5,g56a1a4eaf3+fd7ad03fde,g64539dfbff+f66ac109ec,g67b6fd64d1+0d594cb711,g67fd3c3899+f66ac109ec,g6985122a63+0d594cb711,g74acd417e5+3098891321,g786e29fd12+668abc6043,g81db2e9a8d+98e2ab9f28,g87389fa792+8856018cbb,g89139ef638+0d594cb711,g8d7436a09f+80fda9ce03,g8ea07a8fe4+760ca7c3fc,g90f42f885a+033b1d468d,g97be763408+a8a29bda4b,g99822b682c+e3ec3c61f9,g9d5c6a246b+0d5dac0c3d,ga41d0fce20+9243b26dd2,gbf99507273+8c5ae1fdc5,gd7ef33dd92+0d594cb711,gdab6d2f7ff+3098891321,ge410e46f29+0d594cb711,geaed405ab2+c4bbc419c6,gf9a733ac38+8c5ae1fdc5,w.2025.38
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
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) {
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}}
458} // namespace lsst::cpputils::python
459
460#endif
#define LSST_PRIVATE
Make a symbol hidden even if default visiblity is public.
Definition base.h:49
WrapperCollection(pybind11::module module_, std::string const &package)
Construct a new WrapperCollection.
Definition python.h:264
void addSignatureDependency(std::string const &name)
Indicate an external module that provides a type used in function/method signatures.
Definition python.h:357
std::function< void(pybind11::module &)> WrapperCallback
Function handle type used to hold deferred wrapper declaration functions.
Definition python.h:247
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
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
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
pybind11::class_< T, E > declareException(pybind11::module &mod, const std::string &name, const std::string &base)
Helper function for pybind11, used to define new types of exceptions.
Definition Exception.h:52
T str(T... args)