Belle II Software development
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 /*
43 * We need forward declarations here, because otherwise the compiler ends up calling
44 * the wrong function!
45 */
47 bool checkPythonObject(const boost::python::object& pyObject, bool);
49 bool checkPythonObject(const boost::python::object& pyObject, float);
51 bool checkPythonObject(const boost::python::object& pyObject, double);
53 bool checkPythonObject(const boost::python::object& pyObject, int);
55 bool checkPythonObject(const boost::python::object& pyObject, unsigned int);
57 bool checkPythonObject(const boost::python::object& pyObject, unsigned long int);
59 bool checkPythonObject(const boost::python::object& pyObject, const std::string&);
60 template<typename Key, typename Value>
61 bool checkPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>&);
63 template<typename Value>
64 bool checkPythonObject(const boost::python::object& pyObject, const std::vector<Value>&);
66 template<typename Value>
67 bool checkPythonObject(const boost::python::object& pyObject, const std::set<Value>&);
69 template<typename... Types>
70 bool checkPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>&);
72 template<typename... Types>
73 bool checkPythonObject(const boost::python::object& pyObject, const boost::variant<Types...>&);
75 template<typename Type>
76 bool checkPythonObject(const boost::python::object& pyObject, const std::optional<Type>&);
77
79 template<typename Scalar>
80 Scalar convertPythonObject(const boost::python::object& pyObject, Scalar);
82 template<typename T>
83 std::shared_ptr<T> convertPythonObject(const boost::python::object& pyObject, const std::shared_ptr<T>&);
85 template<typename Key, typename Value>
86 std::map<Key, Value> convertPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>&);
88 template<typename Value>
89 std::vector<Value> convertPythonObject(const boost::python::object& pyObject, const std::vector<Value>&);
91 template<typename Value>
92 std::set<Value> convertPythonObject(const boost::python::object& pyObject, const std::set<Value>&);
94 template<typename... Types>
95 std::tuple<Types...> convertPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>&);
97 template<typename... Types>
98 boost::variant<Types...> convertPythonObject(const boost::python::object& pyObject, const boost::variant<Types...>&);
100 template<typename Type>
101 std::optional<Type> convertPythonObject(const boost::python::object& pyObject, const std::optional<Type>&);
102
103 template<typename Scalar>
104 boost::python::object convertToPythonObject(const Scalar& value);
105 template<typename Value>
106 boost::python::list convertToPythonObject(const std::vector<Value>& vector);
108 template<typename Value>
109 boost::python::object convertToPythonObject(const std::set<Value>& set);
110 template<typename Key, typename Value>
111 boost::python::dict convertToPythonObject(const std::map<Key, Value>& map);
112 template<typename... Types>
113 boost::python::tuple convertToPythonObject(const std::tuple<Types...>& tuple);
114 template<typename... Types>
115 boost::python::object convertToPythonObject(const boost::variant<Types...>& tuple);
117 template<typename Type>
118 boost::python::object convertToPythonObject(const std::optional<Type>& optional);
119
120 template<typename T> struct Type;
121 template<typename T> struct Type<std::vector<T> >;
122 template<typename T> struct Type<std::set<T>>;
123 template<typename A, typename B> struct Type<std::map<A, B> >;
124 template<typename... Types> struct Type<std::tuple<Types...> >;
125 template<typename... Types> struct Type<boost::variant<Types...> >;
126 template<> struct Type<int>;
127 template<> struct Type<bool>;
128 template<> struct Type<float>;
129 template<> struct Type<double>;
130 template<> struct Type<std::string>;
131
132 template< typename T, typename... Types> struct VariadicType;
134 template< typename T> struct VariadicType<T> { static std::string name() { return Type<T>::name(); } };
136 template< typename T, typename... Types> struct VariadicType {
138 static std::string name()
139 {
141 }
142 };
143
148 template<typename T> struct Type { static std::string name() { return "???";} };
150 template<typename T> struct Type<std::vector<T> > { static std::string name() { return std::string("list(") + Type<T>::name() + ")"; } };
152 template<typename T> struct Type<std::set<T>> { static std::string name() { return std::string("set(") + Type<T>::name() + ")"; }};
154 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() + ")"; } };
155
157 template<> struct Type<unsigned int> { static std::string name() { return "unsigned int"; } };
159 template<> struct Type<unsigned long int> { static std::string name() { return "unsigned long int"; } };
161 template<> struct Type<int> { static std::string name() { return "int"; } };
163 template<> struct Type<bool> { static std::string name() { return "bool"; } };
165 template<> struct Type<float> { static std::string name() { return "float"; } };
167 template<> struct Type<double> { static std::string name() { return "float"; } };
169 template<> struct Type<std::string> { static std::string name() { return "str"; } };
170
172 template<typename T> struct Type<std::optional<T>> { static std::string name() { return Type<T>::name() + " or None"; } };
173
175 template<typename... Types> struct Type<std::tuple<Types...> > { static std::string name() { return std::string("tuple(") + VariadicType<Types...>::name() + ")"; } };
176
178 template<typename... Types> struct Type<boost::variant<Types...> > { static std::string name() { return std::string("variant(") + VariadicType<Types...>::name() + ")"; } };
179
181 template<> struct Type<std::shared_ptr<Path>> {
183 static std::string name() { return std::string("Path"); }
184 };
185
186
188 template < size_t > struct SizeT { };
189
193
195 inline bool checkPythonObject(const boost::python::object& pyObject, bool /*dispatch tag*/)
196 {
197 return PyBool_Check(pyObject.ptr());
198 }
199
201 inline bool checkPythonObject(const boost::python::object& pyObject, int /*dispatch tag*/)
202 {
203 return PyLong_CheckExact(pyObject.ptr());
204 }
205
206 inline bool checkPythonObject(const boost::python::object& pyObject, unsigned int /*dispatch tag*/)
207 {
208 return PyLong_CheckExact(pyObject.ptr());
209 }
210
212 inline bool checkPythonObject(const boost::python::object& pyObject, unsigned long int /*dispatch tag*/)
213 {
214 return PyLong_CheckExact(pyObject.ptr());
215 }
216
218 inline bool checkPythonObject(const boost::python::object& pyObject, float /*dispatch tag*/)
219 {
220 return PyFloat_CheckExact(pyObject.ptr());
221 }
222
224 inline bool checkPythonObject(const boost::python::object& pyObject, double /*dispatch tag*/)
225 {
226 return PyFloat_CheckExact(pyObject.ptr());
227 }
228
230 inline bool checkPythonObject(const boost::python::object& pyObject, const std::string& /*dispatch tag*/)
231 {
232 return PyUnicode_Check(pyObject.ptr());
233 }
234
236 template<typename Key, typename Value>
237 bool checkPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>& /*dispatch tag*/)
238 {
239 if (not PyDict_Check(pyObject.ptr())) return false;
240 const boost::python::dict& pyDict = static_cast<const boost::python::dict&>(pyObject);
241 boost::python::list keys = pyDict.keys();
242 boost::python::list values = pyDict.values();
243 for (int i = 0; i < boost::python::len(keys); ++i) {
244 if (not checkPythonObject(keys[i], Key())) return false;
245 if (not checkPythonObject(values[i], Value())) return false;
246 }
247 return true;
248 }
249
257 template<class Functor>
258 bool iteratePythonObject(const boost::python::object& pyObject, Functor function)
259 {
260 boost::python::object iterator(boost::python::handle<>(PyObject_GetIter(pyObject.ptr())));
261 PyObject* item{nullptr};
262 // ok, loop over the iterator and check all elements
263 while ((item = PyIter_Next(iterator.ptr()))) {
264 boost::python::object obj{boost::python::handle<>(item)}; // to make sure we properly decref
265 if (not function(obj)) return false;
266 }
267 return true;
268 }
269
271 template<typename Value>
272 bool checkPythonObject(const boost::python::object& pyObject, const std::vector<Value>& /*dispatch tag*/)
273 {
274 if (not PyList_Check(pyObject.ptr())) return false;
275 return iteratePythonObject(pyObject, [](const boost::python::object & element) {
276 return checkPythonObject(element, Value());
277 });
278 }
279
280 template<typename Value>
281 bool checkPythonObject(const boost::python::object& pyObject, const std::set<Value>&)
282 {
283 if (not PyAnySet_Check(pyObject.ptr())) return false;
284 return iteratePythonObject(pyObject, [](const boost::python::object & element) {
285 return checkPythonObject(element, Value());
286 });
287 }
288
290 template<typename... Types, std::size_t ... Is>
291 bool CheckTuple(const std::tuple<Types...>& tuple, const boost::python::tuple& pyTuple,
292 std::index_sequence<Is...>)
293 {
294 return (... && checkPythonObject(pyTuple[Is], std::get<Is>(tuple)));
295 }
296
298 template<typename... Types>
299 bool checkPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>& tuple)
300 {
301 if (not PyTuple_Check(pyObject.ptr())) return false;
302 const boost::python::tuple& pyTuple = static_cast<const boost::python::tuple&>(pyObject);
303 return CheckTuple(tuple, pyTuple, std::index_sequence_for<Types ...>());
304 }
305
307 template<typename VariantType>
308 bool CheckVariant(const VariantType&,
309 const boost::python::object&,
310 SizeT<0>)
311 {
312 return false;
313 }
314
316 template<typename... Types, size_t N>
317 bool CheckVariant(const boost::variant<Types...>& variant,
318 const boost::python::object& pyObject,
319 SizeT<N>)
320 {
321 using Scalar = typename std::tuple_element < N - 1, std::tuple<Types...> >::type;
322 if (checkPythonObject(pyObject, Scalar())) {
323 return true;
324 }
325 return CheckVariant(variant, pyObject, SizeT < N - 1 > ());
326 }
327
329 template<typename... Types>
330 bool checkPythonObject(const boost::python::object& pyObject, const boost::variant<Types...> variant)
331 {
332 return CheckVariant(variant, pyObject, SizeT<sizeof...(Types)>());
333 }
334
338 template<typename Type>
339 bool checkPythonObject(const boost::python::object& pyObject, const std::optional<Type>&)
340 {
341 return pyObject.is_none() or checkPythonObject(pyObject, Type());
342 }
343
347
354 template<typename Scalar>
355 boost::python::object convertToPythonObject(const Scalar& value)
356 {
357 return boost::python::object(value);
358 }
359
366 template<typename Value>
367 boost::python::list convertToPythonObject(const std::vector<Value>& vector)
368 {
369 boost::python::list outputList;
370 for (const auto& value : vector) {
371 outputList.append(convertToPythonObject(value));
372 }
373 return outputList;
374 }
375
380 template<typename Value>
381 boost::python::object convertToPythonObject(const std::set<Value>& set)
382 {
383 boost::python::object result(boost::python::handle<>(PySet_New(nullptr)));
384 for (const auto& value : set) {
385 PySet_Add(result.ptr(), convertToPythonObject(value).ptr());
386 }
387 return result;
388 }
389
396 template<typename Key, typename Value>
397 boost::python::dict convertToPythonObject(const std::map<Key, Value>& map)
398 {
399 boost::python::dict outputDict;
400 for (auto pair : map) {
401 outputDict[convertToPythonObject(pair.first)] = convertToPythonObject(pair.second);
402 }
403 return outputDict;
404 }
405
414 template < typename TupleType >
415 inline void GetTuple(const TupleType& tuple, boost::python::list& pyList)
416 {
417 GetTuple(tuple, pyList, SizeT<std::tuple_size<TupleType>::value>());
418 }
419
421 template < typename TupleType >
422 inline void GetTuple(const TupleType&, boost::python::list&, SizeT<0>) { }
423
426 template < typename TupleType, size_t N >
427 inline void GetTuple(const TupleType& tuple, boost::python::list& pyList, SizeT<N>)
428 {
429 GetTuple(tuple, pyList, SizeT < N - 1 > ());
430 pyList.append(convertToPythonObject(std::get < N - 1 > (tuple)));
431 }
432
433
440 template<typename... Types>
441 boost::python::tuple convertToPythonObject(const std::tuple<Types...>& tuple)
442 {
443 boost::python::list outputList;
444 GetTuple(tuple, outputList);
445 boost::python::tuple outputTuple(outputList);
446 return outputTuple;
447 }
448
452 class convertToPythonObjectVisitor : public boost::static_visitor<boost::python::object> {
453 public:
455 template<class T>
456 boost::python::object operator()(const T& value) const
457 {
458 return convertToPythonObject(value);
459 }
460 };
461
468 template<typename... Types>
469 boost::python::object convertToPythonObject(const boost::variant<Types...>& variant)
470 {
471 return boost::apply_visitor(convertToPythonObjectVisitor(), variant);
472 }
473
481 template<typename Type>
482 boost::python::object convertToPythonObject(const std::optional<Type>& optional)
483 {
484 if (optional) {
485 return convertToPythonObject(*optional);
486 } else {
487 return boost::python::object();
488 }
489 }
490
494
502 template<typename Scalar>
503 Scalar convertPythonObject(const boost::python::object& pyObject, const Scalar)
504 {
505
506 Scalar tmpValue;
507 boost::python::extract<Scalar> valueProxy(pyObject);
508 if (valueProxy.check()) {
509 tmpValue = static_cast<Scalar>(valueProxy);
510 } else {
511 throw std::runtime_error(std::string("Could not convert value: Expected type '") + Type<Scalar>::name() + "' instead of '" +
512 pyObject.ptr()->ob_type->tp_name + "'.");
513 }
514 return tmpValue;
515
516 }
517
525 template <typename T>
526 __attribute__((noinline))
527 std::shared_ptr<T> convertPythonObject(const boost::python::object& pyObject, const std::shared_ptr<T>&)
528 {
529
530 std::shared_ptr<T> tmpValue;
531 boost::python::extract<std::shared_ptr<T>> valueProxy(pyObject);
532 if (valueProxy.check()) {
533 tmpValue = valueProxy();
534 } else {
535 throw std::runtime_error(
536 std::string("Could not convert value: Expected type '") + Type<std::shared_ptr<T>>::name() + "' instead of '" +
537 pyObject.ptr()->ob_type->tp_name + "'."
538 );
539 }
540 return tmpValue;
541
542 }
543
551 template<typename Value>
552 std::vector<Value> convertPythonObject(const boost::python::object& pyObject, const std::vector<Value>&)
553 {
554
555 std::vector<Value> tmpVector;
556 if (PyList_Check(pyObject.ptr()) or PyGen_Check(pyObject.ptr())) {
557 iteratePythonObject(pyObject, [&tmpVector](const boost::python::object & element) {
558 tmpVector.emplace_back(convertPythonObject(element, Value()));
559 return true;
560 });
561 } else {
562 tmpVector.emplace_back(convertPythonObject(pyObject, Value()));
563 }
564 return tmpVector;
565 }
566
568 template<typename Value>
569 std::set<Value> convertPythonObject(const boost::python::object& pyObject, const std::set<Value>&)
570 {
571 std::set<Value> result;
572 if (PyAnySet_Check(pyObject.ptr())) {
573 iteratePythonObject(pyObject, [&result](const boost::python::object & element) {
574 result.emplace(convertPythonObject(element, Value()));
575 return true;
576 });
577 } else {
578 result.emplace(convertPythonObject(pyObject, Value()));
579 }
580 return result;
581 }
582
583
590 template<typename Key, typename Value>
591 std::map<Key, Value> convertPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>&)
592 {
593 std::map<Key, Value> tmpMap;
594 const boost::python::dict& pyDict = static_cast<const boost::python::dict&>(pyObject);
595 boost::python::list keys = pyDict.keys();
596
597 for (int i = 0; i < boost::python::len(keys); ++i) {
598 Key key = convertPythonObject(keys[boost::python::object(i)], Key());
599 Value value = convertPythonObject(pyDict[key], Value());
600 tmpMap.insert(std::pair<Key, Value>(key, value));
601 }
602 return tmpMap;
603 }
604
605
614 template < typename TupleType >
615 inline void SetTuple(TupleType& tuple, const boost::python::tuple& pyTuple)
616 {
617 static const unsigned N = std::tuple_size<TupleType>::value;
618 if ((unsigned)boost::python::len(pyTuple) != N) {
619 throw std::runtime_error(std::string("Given python tuple has length ") +
620 std::to_string(boost::python::len(pyTuple)) +
621 ", expected " + std::to_string(N));
622 }
623 SetTuple(tuple, pyTuple, SizeT<N>());
624 }
625
627 template < typename TupleType >
628 inline void SetTuple(TupleType&, const boost::python::tuple&, SizeT<0>) { }
629
632 template < typename TupleType, size_t N >
633 inline void SetTuple(TupleType& tuple, const boost::python::tuple& pyTuple, SizeT<N>)
634 {
635 SetTuple(tuple, pyTuple, SizeT < N - 1 > ());
636 std::get < N - 1 > (tuple) = convertPythonObject(pyTuple[N - 1], std::get < N - 1 > (tuple));
637 }
638
639
646 template<typename... Types>
647 std::tuple<Types...> convertPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>&)
648 {
649 std::tuple<Types...> tmpTuple;
650 const boost::python::tuple& pyTuple = static_cast<const boost::python::tuple&>(pyObject);
651 SetTuple(tmpTuple, pyTuple);
652 return tmpTuple;
653 }
654
663 template <typename... Types>
664 inline void SetVariant(boost::variant<Types...>&, const boost::python::object& pyObject, SizeT<0>)
665 {
666 throw std::runtime_error(std::string("Could not set module parameter: Expected type '") +
667 Type<boost::variant<Types...> >::name() + "' instead of '" +
668 pyObject.ptr()->ob_type->tp_name + "'.");
669 }
670
672 template < typename... Types, size_t N >
673 inline void SetVariant(boost::variant<Types...>& variant, const boost::python::object& pyObject, SizeT<N>)
674 {
675 using Scalar = typename std::tuple_element < N - 1, std::tuple<Types...> >::type;
676 if (checkPythonObject(pyObject, Scalar())) {
677 try {
678 Scalar value = convertPythonObject(pyObject, Scalar());
679 variant = value;
680 return;
681 } catch (std::runtime_error& e) {
682 }
683 }
684 // Conversion failed - try next type
685 SetVariant(variant, pyObject, SizeT < N - 1 > ());
686 }
687
688
695 template<typename... Types>
696 boost::variant<Types...> convertPythonObject(const boost::python::object& pyObject, const boost::variant<Types...>&)
697 {
698 boost::variant<Types...> tmpVariant;
699 SetVariant(tmpVariant, pyObject, SizeT<sizeof...(Types)>());
700 return tmpVariant;
701 }
702
709 template<typename Type>
710 std::optional<Type> convertPythonObject(const boost::python::object& pyObject, const std::optional<Type>&)
711 {
712 std::optional<Type> tmpOptional = std::nullopt;
713 if (!pyObject.is_none()) {
714 tmpOptional = convertPythonObject(pyObject, Type());
715 }
716 return tmpOptional;
717 }
718 }
719
720}
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.
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.