Belle II Software light-2607-kasei
PyObjConvUtils.h
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8
9#pragma once
10
11#include <boost/python/object.hpp>
12#include <boost/python/list.hpp>
13#include <boost/python/tuple.hpp>
14#include <boost/python/dict.hpp>
15#include <boost/python/extract.hpp>
16#include <boost/variant.hpp>
17
18#include <map>
19#include <optional>
20#include <set>
21#include <string>
22#include <vector>
23
24
25namespace Belle2 {
30 class Path;
31
40 namespace PyObjConvUtils {
41
42// Throughout this header, values are converted out of Python objects via
43// boost::python::extract. For std::string (and containers/variants of it) this
44// triggers a false-positive -Wmaybe-uninitialized in GCC.
45#if defined(__GNUC__) && !defined(__clang__)
46#pragma GCC diagnostic push
47#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
48#endif
49
50 /*
51 * We need forward declarations here, because otherwise the compiler ends up calling
52 * the wrong function!
53 */
55 bool checkPythonObject(const boost::python::object& pyObject, bool);
57 bool checkPythonObject(const boost::python::object& pyObject, float);
59 bool checkPythonObject(const boost::python::object& pyObject, double);
61 bool checkPythonObject(const boost::python::object& pyObject, int);
63 bool checkPythonObject(const boost::python::object& pyObject, unsigned int);
65 bool checkPythonObject(const boost::python::object& pyObject, unsigned long int);
67 bool checkPythonObject(const boost::python::object& pyObject, const std::string&);
68 template<typename Key, typename Value>
69 bool checkPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>&);
71 template<typename Value>
72 bool checkPythonObject(const boost::python::object& pyObject, const std::vector<Value>&);
74 template<typename Value>
75 bool checkPythonObject(const boost::python::object& pyObject, const std::set<Value>&);
77 template<typename... Types>
78 bool checkPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>&);
80 template<typename... Types>
81 bool checkPythonObject(const boost::python::object& pyObject, const boost::variant<Types...>&);
83 template<typename Type>
84 bool checkPythonObject(const boost::python::object& pyObject, const std::optional<Type>&);
85
87 template<typename Scalar>
88 Scalar convertPythonObject(const boost::python::object& pyObject, Scalar);
90 template<typename T>
91 std::shared_ptr<T> convertPythonObject(const boost::python::object& pyObject, const std::shared_ptr<T>&);
93 template<typename Key, typename Value>
94 std::map<Key, Value> convertPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>&);
96 template<typename Value>
97 std::vector<Value> convertPythonObject(const boost::python::object& pyObject, const std::vector<Value>&);
99 template<typename Value>
100 std::set<Value> convertPythonObject(const boost::python::object& pyObject, const std::set<Value>&);
102 template<typename... Types>
103 std::tuple<Types...> convertPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>&);
105 template<typename... Types>
106 boost::variant<Types...> convertPythonObject(const boost::python::object& pyObject, const boost::variant<Types...>&);
108 template<typename Type>
109 std::optional<Type> convertPythonObject(const boost::python::object& pyObject, const std::optional<Type>&);
110
111 template<typename Scalar>
112 boost::python::object convertToPythonObject(const Scalar& value);
113 template<typename Value>
114 boost::python::list convertToPythonObject(const std::vector<Value>& vector);
116 template<typename Value>
117 boost::python::object convertToPythonObject(const std::set<Value>& set);
118 template<typename Key, typename Value>
119 boost::python::dict convertToPythonObject(const std::map<Key, Value>& map);
120 template<typename... Types>
121 boost::python::tuple convertToPythonObject(const std::tuple<Types...>& tuple);
122 template<typename... Types>
123 boost::python::object convertToPythonObject(const boost::variant<Types...>& tuple);
125 template<typename Type>
126 boost::python::object convertToPythonObject(const std::optional<Type>& optional);
127
128 template<typename T> struct Type;
129 template<typename T> struct Type<std::vector<T> >;
130 template<typename T> struct Type<std::set<T>>;
131 template<typename A, typename B> struct Type<std::map<A, B> >;
132 template<typename... Types> struct Type<std::tuple<Types...> >;
133 template<typename... Types> struct Type<boost::variant<Types...> >;
134 template<> struct Type<int>;
135 template<> struct Type<bool>;
136 template<> struct Type<float>;
137 template<> struct Type<double>;
138 template<> struct Type<std::string>;
139
140 template< typename T, typename... Types> struct VariadicType;
142 template< typename T> struct VariadicType<T> { static std::string name() { return Type<T>::name(); } };
144 template< typename T, typename... Types> struct VariadicType {
146 static std::string name()
147 {
149 }
150 };
151
156 template<typename T> struct Type { static std::string name() { return "???";} };
158 template<typename T> struct Type<std::vector<T> > { static std::string name() { return std::string("list(") + Type<T>::name() + ")"; } };
160 template<typename T> struct Type<std::set<T>> { static std::string name() { return std::string("set(") + Type<T>::name() + ")"; }};
162 template<typename A, typename B> struct Type<std::map<A, B> > { static std::string name() { return std::string("dict(") + Type<A>::name() + " -> " + Type<B>::name() + ")"; } };
163
165 template<> struct Type<unsigned int> { static std::string name() { return "unsigned int"; } };
167 template<> struct Type<unsigned long int> { static std::string name() { return "unsigned long int"; } };
169 template<> struct Type<int> { static std::string name() { return "int"; } };
171 template<> struct Type<bool> { static std::string name() { return "bool"; } };
173 template<> struct Type<float> { static std::string name() { return "float"; } };
175 template<> struct Type<double> { static std::string name() { return "float"; } };
177 template<> struct Type<std::string> { static std::string name() { return "str"; } };
178
180 template<typename T> struct Type<std::optional<T>> { static std::string name() { return Type<T>::name() + " or None"; } };
181
183 template<typename... Types> struct Type<std::tuple<Types...> > { static std::string name() { return std::string("tuple(") + VariadicType<Types...>::name() + ")"; } };
184
186 template<typename... Types> struct Type<boost::variant<Types...> > { static std::string name() { return std::string("variant(") + VariadicType<Types...>::name() + ")"; } };
187
189 template<> struct Type<std::shared_ptr<Path>> {
191 static std::string name() { return std::string("Path"); }
192 };
193
194
196 template < size_t > struct SizeT { };
197
201
203 inline bool checkPythonObject(const boost::python::object& pyObject, bool /*dispatch tag*/)
204 {
205 return PyBool_Check(pyObject.ptr());
206 }
207
209 inline bool checkPythonObject(const boost::python::object& pyObject, int /*dispatch tag*/)
210 {
211 return PyLong_CheckExact(pyObject.ptr());
212 }
213
214 inline bool checkPythonObject(const boost::python::object& pyObject, unsigned int /*dispatch tag*/)
215 {
216 return PyLong_CheckExact(pyObject.ptr());
217 }
218
220 inline bool checkPythonObject(const boost::python::object& pyObject, unsigned long int /*dispatch tag*/)
221 {
222 return PyLong_CheckExact(pyObject.ptr());
223 }
224
226 inline bool checkPythonObject(const boost::python::object& pyObject, float /*dispatch tag*/)
227 {
228 return PyFloat_CheckExact(pyObject.ptr());
229 }
230
232 inline bool checkPythonObject(const boost::python::object& pyObject, double /*dispatch tag*/)
233 {
234 return PyFloat_CheckExact(pyObject.ptr());
235 }
236
238 inline bool checkPythonObject(const boost::python::object& pyObject, const std::string& /*dispatch tag*/)
239 {
240 return PyUnicode_Check(pyObject.ptr());
241 }
242
244 template<typename Key, typename Value>
245 bool checkPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>& /*dispatch tag*/)
246 {
247 if (not PyDict_Check(pyObject.ptr())) return false;
248 const boost::python::dict& pyDict = static_cast<const boost::python::dict&>(pyObject);
249 boost::python::list keys = pyDict.keys();
250 boost::python::list values = pyDict.values();
251 for (int i = 0; i < boost::python::len(keys); ++i) {
252 if (not checkPythonObject(keys[i], Key())) return false;
253 if (not checkPythonObject(values[i], Value())) return false;
254 }
255 return true;
256 }
257
265 template<class Functor>
266 bool iteratePythonObject(const boost::python::object& pyObject, Functor function)
267 {
268 boost::python::object iterator(boost::python::handle<>(PyObject_GetIter(pyObject.ptr())));
269 PyObject* item{nullptr};
270 // ok, loop over the iterator and check all elements
271 while ((item = PyIter_Next(iterator.ptr()))) {
272 boost::python::object obj{boost::python::handle<>(item)}; // to make sure we properly decref
273 if (not function(obj)) return false;
274 }
275 return true;
276 }
277
279 template<typename Value>
280 bool checkPythonObject(const boost::python::object& pyObject, const std::vector<Value>& /*dispatch tag*/)
281 {
282 if (not PyList_Check(pyObject.ptr())) return false;
283 return iteratePythonObject(pyObject, [](const boost::python::object & element) {
284 return checkPythonObject(element, Value());
285 });
286 }
287
288 template<typename Value>
289 bool checkPythonObject(const boost::python::object& pyObject, const std::set<Value>&)
290 {
291 if (not PyAnySet_Check(pyObject.ptr())) return false;
292 return iteratePythonObject(pyObject, [](const boost::python::object & element) {
293 return checkPythonObject(element, Value());
294 });
295 }
296
298 template<typename... Types, std::size_t ... Is>
299 bool CheckTuple(const std::tuple<Types...>& tuple, const boost::python::tuple& pyTuple,
300 std::index_sequence<Is...>)
301 {
302 return (... && checkPythonObject(pyTuple[Is], std::get<Is>(tuple)));
303 }
304
306 template<typename... Types>
307 bool checkPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>& tuple)
308 {
309 if (not PyTuple_Check(pyObject.ptr())) return false;
310 const boost::python::tuple& pyTuple = static_cast<const boost::python::tuple&>(pyObject);
311 return CheckTuple(tuple, pyTuple, std::index_sequence_for<Types ...>());
312 }
313
315 template<typename VariantType>
316 bool CheckVariant(const VariantType&,
317 const boost::python::object&,
318 SizeT<0>)
319 {
320 return false;
321 }
322
324 template<typename... Types, size_t N>
325 bool CheckVariant(const boost::variant<Types...>& variant,
326 const boost::python::object& pyObject,
327 SizeT<N>)
328 {
329 using Scalar = typename std::tuple_element < N - 1, std::tuple<Types...> >::type;
330 if (checkPythonObject(pyObject, Scalar())) {
331 return true;
332 }
333 return CheckVariant(variant, pyObject, SizeT < N - 1 > ());
334 }
335
337 template<typename... Types>
338 bool checkPythonObject(const boost::python::object& pyObject, const boost::variant<Types...> variant)
339 {
340 return CheckVariant(variant, pyObject, SizeT<sizeof...(Types)>());
341 }
342
346 template<typename Type>
347 bool checkPythonObject(const boost::python::object& pyObject, const std::optional<Type>&)
348 {
349 return pyObject.is_none() or checkPythonObject(pyObject, Type());
350 }
351
355
362 template<typename Scalar>
363 boost::python::object convertToPythonObject(const Scalar& value)
364 {
365 return boost::python::object(value);
366 }
367
374 template<typename Value>
375 boost::python::list convertToPythonObject(const std::vector<Value>& vector)
376 {
377 boost::python::list outputList;
378 for (const auto& value : vector) {
379 outputList.append(convertToPythonObject(value));
380 }
381 return outputList;
382 }
383
388 template<typename Value>
389 boost::python::object convertToPythonObject(const std::set<Value>& set)
390 {
391 boost::python::object result(boost::python::handle<>(PySet_New(nullptr)));
392 for (const auto& value : set) {
393 PySet_Add(result.ptr(), convertToPythonObject(value).ptr());
394 }
395 return result;
396 }
397
404 template<typename Key, typename Value>
405 boost::python::dict convertToPythonObject(const std::map<Key, Value>& map)
406 {
407 boost::python::dict outputDict;
408 for (auto pair : map) {
409 outputDict[convertToPythonObject(pair.first)] = convertToPythonObject(pair.second);
410 }
411 return outputDict;
412 }
413
422 template < typename TupleType >
423 inline void GetTuple(const TupleType& tuple, boost::python::list& pyList)
424 {
425 GetTuple(tuple, pyList, SizeT<std::tuple_size<TupleType>::value>());
426 }
427
429 template < typename TupleType >
430 inline void GetTuple(const TupleType&, boost::python::list&, SizeT<0>) { }
431
434 template < typename TupleType, size_t N >
435 inline void GetTuple(const TupleType& tuple, boost::python::list& pyList, SizeT<N>)
436 {
437 GetTuple(tuple, pyList, SizeT < N - 1 > ());
438 pyList.append(convertToPythonObject(std::get < N - 1 > (tuple)));
439 }
440
441
448 template<typename... Types>
449 boost::python::tuple convertToPythonObject(const std::tuple<Types...>& tuple)
450 {
451 boost::python::list outputList;
452 GetTuple(tuple, outputList);
453 boost::python::tuple outputTuple(outputList);
454 return outputTuple;
455 }
456
460 class convertToPythonObjectVisitor : public boost::static_visitor<boost::python::object> {
461 public:
463 template<class T>
464 boost::python::object operator()(const T& value) const
465 {
466 return convertToPythonObject(value);
467 }
468 };
469
476 template<typename... Types>
477 boost::python::object convertToPythonObject(const boost::variant<Types...>& variant)
478 {
479 return boost::apply_visitor(convertToPythonObjectVisitor(), variant);
480 }
481
489 template<typename Type>
490 boost::python::object convertToPythonObject(const std::optional<Type>& optional)
491 {
492 if (optional) {
493 return convertToPythonObject(*optional);
494 } else {
495 return boost::python::object();
496 }
497 }
498
502
510 template<typename Scalar>
511 Scalar convertPythonObject(const boost::python::object& pyObject, const Scalar)
512 {
513
514 Scalar tmpValue;
515 boost::python::extract<Scalar> valueProxy(pyObject);
516 if (valueProxy.check()) {
517 tmpValue = static_cast<Scalar>(valueProxy);
518 } else {
519 throw std::runtime_error(std::string("Could not convert value: Expected type '") + Type<Scalar>::name() + "' instead of '" +
520 pyObject.ptr()->ob_type->tp_name + "'.");
521 }
522 return tmpValue;
523
524 }
525
533 template <typename T>
534 __attribute__((noinline))
535 std::shared_ptr<T> convertPythonObject(const boost::python::object& pyObject, const std::shared_ptr<T>&)
536 {
537
538 std::shared_ptr<T> tmpValue;
539 boost::python::extract<std::shared_ptr<T>> valueProxy(pyObject);
540 if (valueProxy.check()) {
541 tmpValue = valueProxy();
542 } else {
543 throw std::runtime_error(
544 std::string("Could not convert value: Expected type '") + Type<std::shared_ptr<T>>::name() + "' instead of '" +
545 pyObject.ptr()->ob_type->tp_name + "'."
546 );
547 }
548 return tmpValue;
549
550 }
551
559 template<typename Value>
560 std::vector<Value> convertPythonObject(const boost::python::object& pyObject, const std::vector<Value>&)
561 {
562
563 std::vector<Value> tmpVector;
564 if (PyList_Check(pyObject.ptr()) or PyGen_Check(pyObject.ptr())) {
565 iteratePythonObject(pyObject, [&tmpVector](const boost::python::object & element) {
566 tmpVector.emplace_back(convertPythonObject(element, Value()));
567 return true;
568 });
569 } else {
570 tmpVector.emplace_back(convertPythonObject(pyObject, Value()));
571 }
572 return tmpVector;
573 }
574
576 template<typename Value>
577 std::set<Value> convertPythonObject(const boost::python::object& pyObject, const std::set<Value>&)
578 {
579 std::set<Value> result;
580 if (PyAnySet_Check(pyObject.ptr())) {
581 iteratePythonObject(pyObject, [&result](const boost::python::object & element) {
582 result.emplace(convertPythonObject(element, Value()));
583 return true;
584 });
585 } else {
586 result.emplace(convertPythonObject(pyObject, Value()));
587 }
588 return result;
589 }
590
591
598 template<typename Key, typename Value>
599 std::map<Key, Value> convertPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>&)
600 {
601 std::map<Key, Value> tmpMap;
602 const boost::python::dict& pyDict = static_cast<const boost::python::dict&>(pyObject);
603 boost::python::list keys = pyDict.keys();
604
605 for (int i = 0; i < boost::python::len(keys); ++i) {
606 Key key = convertPythonObject(keys[boost::python::object(i)], Key());
607 Value value = convertPythonObject(pyDict[key], Value());
608 tmpMap.insert(std::pair<Key, Value>(key, value));
609 }
610 return tmpMap;
611 }
612
613
622 template < typename TupleType >
623 inline void SetTuple(TupleType& tuple, const boost::python::tuple& pyTuple)
624 {
625 static const unsigned N = std::tuple_size<TupleType>::value;
626 if ((unsigned)boost::python::len(pyTuple) != N) {
627 throw std::runtime_error(std::string("Given python tuple has length ") +
628 std::to_string(boost::python::len(pyTuple)) +
629 ", expected " + std::to_string(N));
630 }
631 SetTuple(tuple, pyTuple, SizeT<N>());
632 }
633
635 template < typename TupleType >
636 inline void SetTuple(TupleType&, const boost::python::tuple&, SizeT<0>) { }
637
640 template < typename TupleType, size_t N >
641 inline void SetTuple(TupleType& tuple, const boost::python::tuple& pyTuple, SizeT<N>)
642 {
643 SetTuple(tuple, pyTuple, SizeT < N - 1 > ());
644 std::get < N - 1 > (tuple) = convertPythonObject(pyTuple[N - 1], std::get < N - 1 > (tuple));
645 }
646
647
654 template<typename... Types>
655 std::tuple<Types...> convertPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>&)
656 {
657 std::tuple<Types...> tmpTuple;
658 const boost::python::tuple& pyTuple = static_cast<const boost::python::tuple&>(pyObject);
659 SetTuple(tmpTuple, pyTuple);
660 return tmpTuple;
661 }
662
671 template <typename... Types>
672 inline void SetVariant(boost::variant<Types...>&, const boost::python::object& pyObject, SizeT<0>)
673 {
674 throw std::runtime_error(std::string("Could not set module parameter: Expected type '") +
675 Type<boost::variant<Types...> >::name() + "' instead of '" +
676 pyObject.ptr()->ob_type->tp_name + "'.");
677 }
678
680 template < typename... Types, size_t N >
681 inline void SetVariant(boost::variant<Types...>& variant, const boost::python::object& pyObject, SizeT<N>)
682 {
683 using Scalar = typename std::tuple_element < N - 1, std::tuple<Types...> >::type;
684 if (checkPythonObject(pyObject, Scalar())) {
685 try {
686 Scalar value = convertPythonObject(pyObject, Scalar());
687 variant = value;
688 return;
689 } catch (const std::runtime_error& e) {
690 }
691 }
692 // Conversion failed - try next type
693 SetVariant(variant, pyObject, SizeT < N - 1 > ());
694 }
695
696
703 template<typename... Types>
704 boost::variant<Types...> convertPythonObject(const boost::python::object& pyObject, const boost::variant<Types...>&)
705 {
706 boost::variant<Types...> tmpVariant;
707 SetVariant(tmpVariant, pyObject, SizeT<sizeof...(Types)>());
708 return tmpVariant;
709 }
710
717 template<typename Type>
718 std::optional<Type> convertPythonObject(const boost::python::object& pyObject, const std::optional<Type>&)
719 {
720 std::optional<Type> tmpOptional = std::nullopt;
721 if (!pyObject.is_none()) {
722 tmpOptional = convertPythonObject(pyObject, Type());
723 }
724 return tmpOptional;
725 }
726#if defined(__GNUC__) && !defined(__clang__)
727#pragma GCC diagnostic pop
728#endif
729 }
730
731}
Implements a path consisting of Module and/or Path objects.
Definition Path.h:38
Helper function object to unpack a value from a variant to a python object.
boost::python::object operator()(const T &value) const
actually convert a value.
STL class.
STL class.
Python object converter utilities namespace.
boost::python::object convertToPythonObject(const Scalar &value)
------------— From C++ TO Python Converter ---------------------—
bool CheckVariant(const VariantType &, const boost::python::object &, SizeT< 0 >)
Recursion sentinel for the case that all former type checks failed for the variant.
bool iteratePythonObject(const boost::python::object &pyObject, Functor function)
Helper function to loop over a python object that implements the iterator concept and call a functor ...
Scalar convertPythonObject(const boost::python::object &pyObject, Scalar)
Convert from Python to given type.
void SetTuple(TupleType &tuple, const boost::python::tuple &pyTuple)
TMP (Template Meta Programming ) The given python tuple is written into the given c++ tuple.
bool CheckTuple(const std::tuple< Types... > &tuple, const boost::python::tuple &pyTuple, std::index_sequence< Is... >)
Check if all tuple elements match.
bool checkPythonObject(const boost::python::object &pyObject, bool)
check if the python object can be converted to the given type.
void GetTuple(const TupleType &tuple, boost::python::list &pyList)
TMP (Template Meta Programming ) The given python list is filled, and later converted into a python t...
void SetVariant(boost::variant< Types... > &, const boost::python::object &pyObject, SizeT< 0 >)
TMP (Template Meta Programming ) The given python object is written into the given c++ boost variant.
Abstract base class for different kinds of events.
STL namespace.
Helper construct for TMP that provides an index at compile time to recurse through type lists.
static std::string name()
type name.
static std::string name()
type name.
static std::string name()
type name.
static std::string name()
type name.
static std::string name()
type name.
Converts a template argument into a string for corresponding Python type.
static std::string name()
type name.
static std::string name()
type name.
Recursively convert multiple types to type names (used for tuples).
static std::string name()
type name.