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 Key, typename Value>
83 std::map<Key, Value> convertPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>&);
85 template<typename Value>
86 std::vector<Value> convertPythonObject(const boost::python::object& pyObject, const std::vector<Value>&);
88 template<typename Value>
89 std::set<Value> convertPythonObject(const boost::python::object& pyObject, const std::set<Value>&);
91 template<typename... Types>
92 std::tuple<Types...> convertPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>&);
94 template<typename... Types>
95 boost::variant<Types...> convertPythonObject(const boost::python::object& pyObject, const boost::variant<Types...>&);
97 template<typename Type>
98 std::optional<Type> convertPythonObject(const boost::python::object& pyObject, const std::optional<Type>&);
99
100 template<typename Scalar>
101 boost::python::object convertToPythonObject(const Scalar& value);
102 template<typename Value>
103 boost::python::list convertToPythonObject(const std::vector<Value>& vector);
105 template<typename Value>
106 boost::python::object convertToPythonObject(const std::set<Value>& set);
107 template<typename Key, typename Value>
108 boost::python::dict convertToPythonObject(const std::map<Key, Value>& map);
109 template<typename... Types>
110 boost::python::tuple convertToPythonObject(const std::tuple<Types...>& tuple);
111 template<typename... Types>
112 boost::python::object convertToPythonObject(const boost::variant<Types...>& tuple);
114 template<typename Type>
115 boost::python::object convertToPythonObject(const std::optional<Type>& optional);
116
117 template<typename T> struct Type;
118 template<typename T> struct Type<std::vector<T> >;
119 template<typename T> struct Type<std::set<T>>;
120 template<typename A, typename B> struct Type<std::map<A, B> >;
121 template<typename... Types> struct Type<std::tuple<Types...> >;
122 template<typename... Types> struct Type<boost::variant<Types...> >;
123 template<> struct Type<int>;
124 template<> struct Type<bool>;
125 template<> struct Type<float>;
126 template<> struct Type<double>;
127 template<> struct Type<std::string>;
128
129 template< typename T, typename... Types> struct VariadicType;
131 template< typename T> struct VariadicType<T> { static std::string name() { return Type<T>::name(); } };
133 template< typename T, typename... Types> struct VariadicType {
135 static std::string name()
136 {
138 }
139 };
140
145 template<typename T> struct Type { static std::string name() { return "???";} };
147 template<typename T> struct Type<std::vector<T> > { static std::string name() { return std::string("list(") + Type<T>::name() + ")"; } };
149 template<typename T> struct Type<std::set<T>> { static std::string name() { return std::string("set(") + Type<T>::name() + ")"; }};
151 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() + ")"; } };
152
154 template<> struct Type<unsigned int> { static std::string name() { return "unsigned int"; } };
156 template<> struct Type<unsigned long int> { static std::string name() { return "unsigned long int"; } };
158 template<> struct Type<int> { static std::string name() { return "int"; } };
160 template<> struct Type<bool> { static std::string name() { return "bool"; } };
162 template<> struct Type<float> { static std::string name() { return "float"; } };
164 template<> struct Type<double> { static std::string name() { return "float"; } };
166 template<> struct Type<std::string> { static std::string name() { return "str"; } };
167
169 template<typename T> struct Type<std::optional<T>> { static std::string name() { return Type<T>::name() + " or None"; } };
170
172 template<typename... Types> struct Type<std::tuple<Types...> > { static std::string name() { return std::string("tuple(") + VariadicType<Types...>::name() + ")"; } };
173
175 template<typename... Types> struct Type<boost::variant<Types...> > { static std::string name() { return std::string("variant(") + VariadicType<Types...>::name() + ")"; } };
176
178 template<> struct Type<std::shared_ptr<Path>> {
180 static std::string name() { return std::string("Path"); }
181 };
182
183
185 template < size_t > struct SizeT { };
186
192 inline bool checkPythonObject(const boost::python::object& pyObject, bool /*dispatch tag*/)
193 {
194 return PyBool_Check(pyObject.ptr());
195 }
196
198 inline bool checkPythonObject(const boost::python::object& pyObject, int /*dispatch tag*/)
199 {
200 return PyLong_CheckExact(pyObject.ptr());
201 }
203 inline bool checkPythonObject(const boost::python::object& pyObject, unsigned int /*dispatch tag*/)
204 {
205 return PyLong_CheckExact(pyObject.ptr());
206 }
207
209 inline bool checkPythonObject(const boost::python::object& pyObject, unsigned long int /*dispatch tag*/)
210 {
211 return PyLong_CheckExact(pyObject.ptr());
212 }
213
215 inline bool checkPythonObject(const boost::python::object& pyObject, float /*dispatch tag*/)
216 {
217 return PyFloat_CheckExact(pyObject.ptr());
218 }
219
221 inline bool checkPythonObject(const boost::python::object& pyObject, double /*dispatch tag*/)
222 {
223 return PyFloat_CheckExact(pyObject.ptr());
224 }
225
227 inline bool checkPythonObject(const boost::python::object& pyObject, const std::string& /*dispatch tag*/)
228 {
229 return PyUnicode_Check(pyObject.ptr());
230 }
231
233 template<typename Key, typename Value>
234 bool checkPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>& /*dispatch tag*/)
235 {
236 if (not PyDict_Check(pyObject.ptr())) return false;
237 const boost::python::dict& pyDict = static_cast<const boost::python::dict&>(pyObject);
238 boost::python::list keys = pyDict.keys();
239 boost::python::list values = pyDict.values();
240 for (int i = 0; i < boost::python::len(keys); ++i) {
241 if (not checkPythonObject(keys[i], Key())) return false;
242 if (not checkPythonObject(values[i], Value())) return false;
243 }
244 return true;
245 }
246
254 template<class Functor>
255 bool iteratePythonObject(const boost::python::object& pyObject, Functor function)
256 {
257 boost::python::object iterator(boost::python::handle<>(PyObject_GetIter(pyObject.ptr())));
258 PyObject* item{nullptr};
259 // ok, loop over the iterator and check all elements
260 while ((item = PyIter_Next(iterator.ptr()))) {
261 boost::python::object obj{boost::python::handle<>(item)}; // to make sure we properly decref
262 if (not function(obj)) return false;
263 }
264 return true;
265 }
266
268 template<typename Value>
269 bool checkPythonObject(const boost::python::object& pyObject, const std::vector<Value>& /*dispatch tag*/)
270 {
271 if (not PyList_Check(pyObject.ptr())) return false;
272 return iteratePythonObject(pyObject, [](const boost::python::object & element) {
273 return checkPythonObject(element, Value());
274 });
275 }
276
277 template<typename Value>
278 bool checkPythonObject(const boost::python::object& pyObject, const std::set<Value>&)
279 {
280 if (not PyAnySet_Check(pyObject.ptr())) return false;
281 return iteratePythonObject(pyObject, [](const boost::python::object & element) {
282 return checkPythonObject(element, Value());
283 });
284 }
285
287 template<typename... Types, std::size_t ... Is>
288 bool CheckTuple(const std::tuple<Types...>& tuple, const boost::python::tuple& pyTuple,
289 std::index_sequence<Is...>)
290 {
291 return (... && checkPythonObject(pyTuple[Is], std::get<Is>(tuple)));
292 }
293
295 template<typename... Types>
296 bool checkPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>& tuple)
297 {
298 if (not PyTuple_Check(pyObject.ptr())) return false;
299 const boost::python::tuple& pyTuple = static_cast<const boost::python::tuple&>(pyObject);
300 return CheckTuple(tuple, pyTuple, std::index_sequence_for<Types ...>());
301 }
302
304 template<typename VariantType>
305 bool CheckVariant(const VariantType&,
306 const boost::python::object&,
307 SizeT<0>)
308 {
309 return false;
310 }
311
313 template<typename... Types, size_t N>
314 bool CheckVariant(const boost::variant<Types...>& variant,
315 const boost::python::object& pyObject,
316 SizeT<N>)
317 {
318 using Scalar = typename std::tuple_element < N - 1, std::tuple<Types...> >::type;
319 if (checkPythonObject(pyObject, Scalar())) {
320 return true;
321 }
322 return CheckVariant(variant, pyObject, SizeT < N - 1 > ());
323 }
324
326 template<typename... Types>
327 bool checkPythonObject(const boost::python::object& pyObject, const boost::variant<Types...> variant)
328 {
329 return CheckVariant(variant, pyObject, SizeT<sizeof...(Types)>());
330 }
331
335 template<typename Type>
336 bool checkPythonObject(const boost::python::object& pyObject, const std::optional<Type>&)
337 {
338 return pyObject.is_none() or checkPythonObject(pyObject, Type());
339 }
340
351 template<typename Scalar>
352 boost::python::object convertToPythonObject(const Scalar& value)
353 {
354 return boost::python::object(value);
355 }
356
363 template<typename Value>
364 boost::python::list convertToPythonObject(const std::vector<Value>& vector)
365 {
366 boost::python::list outputList;
367 for (const auto& value : vector) {
368 outputList.append(convertToPythonObject(value));
369 }
370 return outputList;
371 }
372
377 template<typename Value>
378 boost::python::object convertToPythonObject(const std::set<Value>& set)
379 {
380 boost::python::object result(boost::python::handle<>(PySet_New(nullptr)));
381 for (const auto& value : set) {
382 PySet_Add(result.ptr(), convertToPythonObject(value).ptr());
383 }
384 return result;
385 }
386
393 template<typename Key, typename Value>
394 boost::python::dict convertToPythonObject(const std::map<Key, Value>& map)
395 {
396 boost::python::dict outputDict;
397 for (auto pair : map) {
398 outputDict[convertToPythonObject(pair.first)] = convertToPythonObject(pair.second);
399 }
400 return outputDict;
401 }
402
411 template < typename TupleType >
412 inline void GetTuple(const TupleType& tuple, boost::python::list& pyList)
413 {
414 GetTuple(tuple, pyList, SizeT<std::tuple_size<TupleType>::value>());
415 }
416
418 template < typename TupleType >
419 inline void GetTuple(const TupleType&, boost::python::list&, SizeT<0>) { }
420
423 template < typename TupleType, size_t N >
424 inline void GetTuple(const TupleType& tuple, boost::python::list& pyList, SizeT<N>)
425 {
426 GetTuple(tuple, pyList, SizeT < N - 1 > ());
427 pyList.append(convertToPythonObject(std::get < N - 1 > (tuple)));
428 }
437 template<typename... Types>
438 boost::python::tuple convertToPythonObject(const std::tuple<Types...>& tuple)
439 {
440 boost::python::list outputList;
441 GetTuple(tuple, outputList);
442 boost::python::tuple outputTuple(outputList);
443 return outputTuple;
444 }
445
449 class convertToPythonObjectVisitor : public boost::static_visitor<boost::python::object> {
450 public:
452 template<class T>
453 boost::python::object operator()(const T& value) const
454 {
455 return convertToPythonObject(value);
456 }
457 };
458
465 template<typename... Types>
466 boost::python::object convertToPythonObject(const boost::variant<Types...>& variant)
467 {
468 return boost::apply_visitor(convertToPythonObjectVisitor(), variant);
469 }
470
478 template<typename Type>
479 boost::python::object convertToPythonObject(const std::optional<Type>& optional)
480 {
481 if (optional) {
482 return convertToPythonObject(*optional);
483 } else {
484 return boost::python::object();
485 }
486 }
487
499 template<typename Scalar>
500 Scalar convertPythonObject(const boost::python::object& pyObject, const Scalar)
501 {
502
503 Scalar tmpValue;
504 boost::python::extract<Scalar> valueProxy(pyObject);
505 if (valueProxy.check()) {
506 tmpValue = static_cast<Scalar>(valueProxy);
507 } else {
508 throw std::runtime_error(std::string("Could not convert value: Expected type '") + Type<Scalar>::name() + "' instead of '" +
509 pyObject.ptr()->ob_type->tp_name + "'.");
510 }
511 return tmpValue;
512
513 }
514
522 template<typename Value>
523 std::vector<Value> convertPythonObject(const boost::python::object& pyObject, const std::vector<Value>&)
524 {
525
526 std::vector<Value> tmpVector;
527 if (PyList_Check(pyObject.ptr()) or PyGen_Check(pyObject.ptr())) {
528 iteratePythonObject(pyObject, [&tmpVector](const boost::python::object & element) {
529 tmpVector.emplace_back(convertPythonObject(element, Value()));
530 return true;
531 });
532 } else {
533 tmpVector.emplace_back(convertPythonObject(pyObject, Value()));
534 }
535 return tmpVector;
536 }
537
539 template<typename Value>
540 std::set<Value> convertPythonObject(const boost::python::object& pyObject, const std::set<Value>&)
541 {
542 std::set<Value> result;
543 if (PyAnySet_Check(pyObject.ptr())) {
544 iteratePythonObject(pyObject, [&result](const boost::python::object & element) {
545 result.emplace(convertPythonObject(element, Value()));
546 return true;
547 });
548 } else {
549 result.emplace(convertPythonObject(pyObject, Value()));
550 }
551 return result;
552 }
553
554
561 template<typename Key, typename Value>
562 std::map<Key, Value> convertPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>&)
563 {
564 std::map<Key, Value> tmpMap;
565 const boost::python::dict& pyDict = static_cast<const boost::python::dict&>(pyObject);
566 boost::python::list keys = pyDict.keys();
567
568 for (int i = 0; i < boost::python::len(keys); ++i) {
569 Key key = convertPythonObject(keys[boost::python::object(i)], Key());
570 Value value = convertPythonObject(pyDict[key], Value());
571 tmpMap.insert(std::pair<Key, Value>(key, value));
572 }
573 return tmpMap;
574 }
575
576
585 template < typename TupleType >
586 inline void SetTuple(TupleType& tuple, const boost::python::tuple& pyTuple)
587 {
588 static const unsigned N = std::tuple_size<TupleType>::value;
589 if ((unsigned)boost::python::len(pyTuple) != N) {
590 throw std::runtime_error(std::string("Given python tuple has length ") +
591 std::to_string(boost::python::len(pyTuple)) +
592 ", expected " + std::to_string(N));
593 }
594 SetTuple(tuple, pyTuple, SizeT<N>());
595 }
596
598 template < typename TupleType >
599 inline void SetTuple(TupleType&, const boost::python::tuple&, SizeT<0>) { }
600
603 template < typename TupleType, size_t N >
604 inline void SetTuple(TupleType& tuple, const boost::python::tuple& pyTuple, SizeT<N>)
605 {
606 SetTuple(tuple, pyTuple, SizeT < N - 1 > ());
607 std::get < N - 1 > (tuple) = convertPythonObject(pyTuple[N - 1], std::get < N - 1 > (tuple));
608 }
617 template<typename... Types>
618 std::tuple<Types...> convertPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>&)
619 {
620 std::tuple<Types...> tmpTuple;
621 const boost::python::tuple& pyTuple = static_cast<const boost::python::tuple&>(pyObject);
622 SetTuple(tmpTuple, pyTuple);
623 return tmpTuple;
624 }
625
634 template <typename... Types>
635 inline void SetVariant(boost::variant<Types...>&, const boost::python::object& pyObject, SizeT<0>)
636 {
637 throw std::runtime_error(std::string("Could not set module parameter: Expected type '") +
638 Type<boost::variant<Types...> >::name() + "' instead of '" +
639 pyObject.ptr()->ob_type->tp_name + "'.");
640 }
641
643 template < typename... Types, size_t N >
644 inline void SetVariant(boost::variant<Types...>& variant, const boost::python::object& pyObject, SizeT<N>)
645 {
646 using Scalar = typename std::tuple_element < N - 1, std::tuple<Types...> >::type;
647 if (checkPythonObject(pyObject, Scalar())) {
648 try {
649 Scalar value = convertPythonObject(pyObject, Scalar());
650 variant = value;
651 return;
652 } catch (std::runtime_error& e) {
653 }
654 }
655 // Conversion failed - try next type
656 SetVariant(variant, pyObject, SizeT < N - 1 > ());
657 }
666 template<typename... Types>
667 boost::variant<Types...> convertPythonObject(const boost::python::object& pyObject, const boost::variant<Types...>&)
668 {
669 boost::variant<Types...> tmpVariant;
670 SetVariant(tmpVariant, pyObject, SizeT<sizeof...(Types)>());
671 return tmpVariant;
672 }
673
680 template<typename Type>
681 std::optional<Type> convertPythonObject(const boost::python::object& pyObject, const std::optional<Type>&)
682 {
683 std::optional<Type> tmpOptional = std::nullopt;
684 if (!pyObject.is_none()) {
685 tmpOptional = convertPythonObject(pyObject, Type());
686 }
687 return tmpOptional;
688 }
689 }
691}
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.
boost::python::object convertToPythonObject(const Scalar &value)
------------— From C++ TO Python Converter ---------------------—
bool CheckVariant(const VariantType &, const boost::python::object &, SizeT< 0 >)
Recursion sentinal 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.
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.