Belle II Software  release-06-00-14
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 
17 #include <boost/variant.hpp>
18 #include <boost/optional.hpp>
19 
20 #include <set>
21 #include <map>
22 #include <string>
23 #include <vector>
24 
25 
26 namespace Belle2 {
31  class Path;
32 
41  namespace PyObjConvUtils {
42 
43  /*
44  * We need forward declarations here, because otherwise the compiler ends up calling
45  * the wrong function!
46  */
48  bool checkPythonObject(const boost::python::object& pyObject, bool);
50  bool checkPythonObject(const boost::python::object& pyObject, float);
52  bool checkPythonObject(const boost::python::object& pyObject, double);
54  bool checkPythonObject(const boost::python::object& pyObject, int);
56  bool checkPythonObject(const boost::python::object& pyObject, unsigned int);
58  bool checkPythonObject(const boost::python::object& pyObject, unsigned long int);
60  bool checkPythonObject(const boost::python::object& pyObject, const std::string&);
61  template<typename Key, typename Value>
62  bool checkPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>&);
64  template<typename Value>
65  bool checkPythonObject(const boost::python::object& pyObject, const std::vector<Value>&);
67  template<typename Value>
68  bool checkPythonObject(const boost::python::object& pyObject, const std::set<Value>&);
70  template<typename... Types>
71  bool checkPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>&);
73  template<typename... Types>
74  bool checkPythonObject(const boost::python::object& pyObject, const boost::variant<Types...>&);
76  template<typename Type>
77  bool checkPythonObject(const boost::python::object& pyObject, const boost::optional<Type>&);
78 
80  template<typename Scalar>
81  Scalar convertPythonObject(const boost::python::object& pyObject, Scalar);
83  template<typename Key, typename Value>
84  std::map<Key, Value> convertPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>&);
86  template<typename Value>
87  std::vector<Value> convertPythonObject(const boost::python::object& pyObject, const std::vector<Value>&);
89  template<typename Value>
90  std::set<Value> convertPythonObject(const boost::python::object& pyObject, const std::set<Value>&);
92  template<typename... Types>
93  std::tuple<Types...> convertPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>&);
95  template<typename... Types>
96  boost::variant<Types...> convertPythonObject(const boost::python::object& pyObject, const boost::variant<Types...>&);
98  template<typename Type>
99  boost::optional<Type> convertPythonObject(const boost::python::object& pyObject, const boost::optional<Type>&);
100 
101  template<typename Scalar>
102  boost::python::object convertToPythonObject(const Scalar& value);
103  template<typename Value>
104  boost::python::list convertToPythonObject(const std::vector<Value>& vector);
106  template<typename Value>
107  boost::python::object convertToPythonObject(const std::set<Value>& set);
108  template<typename Key, typename Value>
109  boost::python::dict convertToPythonObject(const std::map<Key, Value>& map);
110  template<typename... Types>
111  boost::python::tuple convertToPythonObject(const std::tuple<Types...>& tuple);
112  template<typename... Types>
113  boost::python::object convertToPythonObject(const boost::variant<Types...>& tuple);
115  template<typename Type>
116  boost::python::object convertToPythonObject(const boost::optional<Type>& optional);
117 
118  template<typename T> struct Type;
119  template<typename T> struct Type<std::vector<T> >;
120  template<typename T> struct Type<std::set<T>>;
121  template<typename A, typename B> struct Type<std::map<A, B> >;
122  template<typename... Types> struct Type<std::tuple<Types...> >;
123  template<typename... Types> struct Type<boost::variant<Types...> >;
124  template<> struct Type<int>;
125  template<> struct Type<bool>;
126  template<> struct Type<float>;
127  template<> struct Type<double>;
128  template<> struct Type<std::string>;
129 
130  template< typename T, typename... Types> struct VariadicType;
132  template< typename T> struct VariadicType<T> { static std::string name() { return Type<T>::name(); } };
134  template< typename T, typename... Types> struct VariadicType {
136  static std::string name()
137  {
138  return Type<T>::name() + ", " + VariadicType<Types...>::name();
139  }
140  };
141 
146  template<typename T> struct Type { static std::string name() { return "???";} };
148  template<typename T> struct Type<std::vector<T> > { static std::string name() { return std::string("list(") + Type<T>::name() + ")"; } };
150  template<typename T> struct Type<std::set<T>> { static std::string name() { return std::string("set(") + Type<T>::name() + ")"; }};
152  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() + ")"; } };
153 
155  template<> struct Type<unsigned int> { static std::string name() { return "unsigned int"; } };
157  template<> struct Type<unsigned long int> { static std::string name() { return "unsigned long int"; } };
159  template<> struct Type<int> { static std::string name() { return "int"; } };
161  template<> struct Type<bool> { static std::string name() { return "bool"; } };
163  template<> struct Type<float> { static std::string name() { return "float"; } };
165  template<> struct Type<double> { static std::string name() { return "float"; } };
167  template<> struct Type<std::string> { static std::string name() { return "str"; } };
168 
170  template<typename T> struct Type<boost::optional<T>> { static std::string name() { return Type<T>::name() + " or None"; } };
171 
173  template<typename... Types> struct Type<std::tuple<Types...> > { static std::string name() { return std::string("tuple(") + VariadicType<Types...>::name() + ")"; } };
174 
176  template<typename... Types> struct Type<boost::variant<Types...> > { static std::string name() { return std::string("variant(") + VariadicType<Types...>::name() + ")"; } };
177 
179  template<> struct Type<std::shared_ptr<Path>> {
181  static std::string name() { return std::string("Path"); }
182  };
183 
184 
186  template < size_t > struct SizeT { };
187 
193  inline bool checkPythonObject(const boost::python::object& pyObject, bool /*dispatch tag*/)
194  {
195  return PyBool_Check(pyObject.ptr());
196  }
197 
199  inline bool checkPythonObject(const boost::python::object& pyObject, int /*dispatch tag*/)
200  {
201  return PyLong_CheckExact(pyObject.ptr());
202  }
204  inline bool checkPythonObject(const boost::python::object& pyObject, unsigned int /*dispatch tag*/)
205  {
206  return PyLong_CheckExact(pyObject.ptr());
207  }
208 
210  inline bool checkPythonObject(const boost::python::object& pyObject, unsigned long int /*dispatch tag*/)
211  {
212  return PyLong_CheckExact(pyObject.ptr());
213  }
214 
216  inline bool checkPythonObject(const boost::python::object& pyObject, float /*dispatch tag*/)
217  {
218  return PyFloat_CheckExact(pyObject.ptr());
219  }
220 
222  inline bool checkPythonObject(const boost::python::object& pyObject, double /*dispatch tag*/)
223  {
224  return PyFloat_CheckExact(pyObject.ptr());
225  }
226 
228  inline bool checkPythonObject(const boost::python::object& pyObject, const std::string& /*dispatch tag*/)
229  {
230  return PyUnicode_Check(pyObject.ptr());
231  }
232 
234  template<typename Key, typename Value>
235  bool checkPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>& /*dispatch tag*/)
236  {
237  if (not PyDict_Check(pyObject.ptr())) return false;
238  const boost::python::dict& pyDict = static_cast<const boost::python::dict&>(pyObject);
239  boost::python::list keys = pyDict.keys();
240  boost::python::list values = pyDict.values();
241  for (int i = 0; i < boost::python::len(keys); ++i) {
242  if (not checkPythonObject(keys[i], Key())) return false;
243  if (not checkPythonObject(values[i], Value())) return false;
244  }
245  return true;
246  }
247 
255  template<class Functor>
256  bool iteratePythonObject(const boost::python::object& pyObject, Functor function)
257  {
258  boost::python::object iterator(boost::python::handle<>(PyObject_GetIter(pyObject.ptr())));
259  PyObject* item{nullptr};
260  // ok, loop over the iterator and check all elements
261  while ((item = PyIter_Next(iterator.ptr()))) {
262  boost::python::object obj{boost::python::handle<>(item)}; // to make sure we properly decref
263  if (not function(obj)) return false;
264  }
265  return true;
266  }
267 
269  template<typename Value>
270  bool checkPythonObject(const boost::python::object& pyObject, const std::vector<Value>& /*dispatch tag*/)
271  {
272  if (not PyList_Check(pyObject.ptr())) return false;
273  return iteratePythonObject(pyObject, [](const boost::python::object & element) {
274  return checkPythonObject(element, Value());
275  });
276  }
277 
278  template<typename Value>
279  bool checkPythonObject(const boost::python::object& pyObject, const std::set<Value>&)
280  {
281  if (not PyAnySet_Check(pyObject.ptr())) return false;
282  return iteratePythonObject(pyObject, [](const boost::python::object & element) {
283  return checkPythonObject(element, Value());
284  });
285  }
286 
288  template<typename... Types, std::size_t ... Is>
289  bool CheckTuple(const std::tuple<Types...>& tuple, const boost::python::tuple& pyTuple,
290  std::index_sequence<Is...>)
291  {
292  return (... && checkPythonObject(pyTuple[Is], std::get<Is>(tuple)));
293  }
294 
296  template<typename... Types>
297  bool checkPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>& tuple)
298  {
299  if (not PyTuple_Check(pyObject.ptr())) return false;
300  const boost::python::tuple& pyTuple = static_cast<const boost::python::tuple&>(pyObject);
301  return CheckTuple(tuple, pyTuple, std::index_sequence_for<Types ...>());
302  }
303 
305  template<typename VariantType>
306  bool CheckVariant(const VariantType&,
307  const boost::python::object&,
308  SizeT<0>)
309  {
310  return false;
311  }
312 
314  template<typename... Types, size_t N>
315  bool CheckVariant(const boost::variant<Types...>& variant,
316  const boost::python::object& pyObject,
317  SizeT<N>)
318  {
319  using Scalar = typename std::tuple_element < N - 1, std::tuple<Types...> >::type;
320  if (checkPythonObject(pyObject , Scalar())) {
321  return true;
322  }
323  return CheckVariant(variant, pyObject, SizeT < N - 1 > ());
324  }
325 
327  template<typename... Types>
328  bool checkPythonObject(const boost::python::object& pyObject, const boost::variant<Types...> variant)
329  {
330  return CheckVariant(variant, pyObject, SizeT<sizeof...(Types)>());
331  }
332 
336  template<typename Type>
337  bool checkPythonObject(const boost::python::object& pyObject, const boost::optional<Type>&)
338  {
339  return pyObject.is_none() or checkPythonObject(pyObject, Type());
340  }
341 
352  template<typename Scalar>
353  boost::python::object convertToPythonObject(const Scalar& value)
354  {
355  return boost::python::object(value);
356  }
357 
364  template<typename Value>
365  boost::python::list convertToPythonObject(const std::vector<Value>& vector)
366  {
367  boost::python::list outputList;
368  for (const auto& value : vector) {
369  outputList.append(convertToPythonObject(value));
370  }
371  return outputList;
372  }
373 
378  template<typename Value>
379  boost::python::object convertToPythonObject(const std::set<Value>& set)
380  {
381  boost::python::object result(boost::python::handle<>(PySet_New(nullptr)));
382  for (const auto& value : set) {
383  PySet_Add(result.ptr(), convertToPythonObject(value).ptr());
384  }
385  return result;
386  }
387 
394  template<typename Key, typename Value>
395  boost::python::dict convertToPythonObject(const std::map<Key, Value>& map)
396  {
397  boost::python::dict outputDict;
398  for (auto pair : map) {
399  outputDict[convertToPythonObject(pair.first)] = convertToPythonObject(pair.second);
400  }
401  return outputDict;
402  }
403 
412  template < typename TupleType >
413  inline void GetTuple(const TupleType& tuple, boost::python::list& pyList)
414  {
415  GetTuple(tuple, pyList, SizeT<std::tuple_size<TupleType>::value>());
416  }
417 
419  template < typename TupleType >
420  inline void GetTuple(const TupleType&, boost::python::list&, SizeT<0>) { }
421 
424  template < typename TupleType, size_t N >
425  inline void GetTuple(const TupleType& tuple, boost::python::list& pyList, SizeT<N>)
426  {
427  GetTuple(tuple, pyList, SizeT < N - 1 > ());
428  pyList.append(convertToPythonObject(std::get < N - 1 > (tuple)));
429  }
438  template<typename... Types>
439  boost::python::tuple convertToPythonObject(const std::tuple<Types...>& tuple)
440  {
441  boost::python::list outputList;
442  GetTuple(tuple, outputList);
443  boost::python::tuple outputTuple(outputList);
444  return outputTuple;
445  }
446 
450  class convertToPythonObjectVisitor : public boost::static_visitor<boost::python::object> {
451  public:
453  template<class T>
454  boost::python::object operator()(const T& value) const
455  {
456  return convertToPythonObject(value);
457  }
458  };
459 
466  template<typename... Types>
467  boost::python::object convertToPythonObject(const boost::variant<Types...>& variant)
468  {
469  return boost::apply_visitor(convertToPythonObjectVisitor(), variant);
470  }
471 
479  template<typename Type>
480  boost::python::object convertToPythonObject(const boost::optional<Type>& optional)
481  {
482  if (optional) {
483  return convertToPythonObject(*optional);
484  } else {
485  return boost::python::object();
486  }
487  }
488 
500  template<typename Scalar>
501  Scalar convertPythonObject(const boost::python::object& pyObject, const Scalar)
502  {
503 
504  Scalar tmpValue;
505  boost::python::extract<Scalar> valueProxy(pyObject);
506  if (valueProxy.check()) {
507  tmpValue = static_cast<Scalar>(valueProxy);
508  } else {
509  throw std::runtime_error(std::string("Could not convert value: Expected type '") + Type<Scalar>::name() + "' instead of '" +
510  pyObject.ptr()->ob_type->tp_name + "'.");
511  }
512  return tmpValue;
513 
514  }
515 
523  template<typename Value>
524  std::vector<Value> convertPythonObject(const boost::python::object& pyObject, const std::vector<Value>&)
525  {
526 
527  std::vector<Value> tmpVector;
528  if (PyList_Check(pyObject.ptr()) or PyGen_Check(pyObject.ptr())) {
529  iteratePythonObject(pyObject, [&tmpVector](const boost::python::object & element) {
530  tmpVector.emplace_back(convertPythonObject(element, Value()));
531  return true;
532  });
533  } else {
534  tmpVector.emplace_back(convertPythonObject(pyObject, Value()));
535  }
536  return tmpVector;
537  }
538 
540  template<typename Value>
541  std::set<Value> convertPythonObject(const boost::python::object& pyObject, const std::set<Value>&)
542  {
543  std::set<Value> result;
544  if (PyAnySet_Check(pyObject.ptr())) {
545  iteratePythonObject(pyObject, [&result](const boost::python::object & element) {
546  result.emplace(convertPythonObject(element, Value()));
547  return true;
548  });
549  } else {
550  result.emplace(convertPythonObject(pyObject, Value()));
551  }
552  return result;
553  }
554 
555 
562  template<typename Key, typename Value>
563  std::map<Key, Value> convertPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>&)
564  {
565  std::map<Key, Value> tmpMap;
566  const boost::python::dict& pyDict = static_cast<const boost::python::dict&>(pyObject);
567  boost::python::list keys = pyDict.keys();
568 
569  for (int i = 0; i < boost::python::len(keys); ++i) {
570  Key key = convertPythonObject(keys[boost::python::object(i)], Key());
571  Value value = convertPythonObject(pyDict[key], Value());
572  tmpMap.insert(std::pair<Key, Value>(key, value));
573  }
574  return tmpMap;
575  }
576 
577 
586  template < typename TupleType >
587  inline void SetTuple(TupleType& tuple, const boost::python::tuple& pyTuple)
588  {
589  static const unsigned N = std::tuple_size<TupleType>::value;
590  if ((unsigned)boost::python::len(pyTuple) != N) {
591  throw std::runtime_error(std::string("Given python tuple has length ") +
592  std::to_string(boost::python::len(pyTuple)) +
593  ", expected " + std::to_string(N));
594  }
595  SetTuple(tuple, pyTuple, SizeT<N>());
596  }
597 
599  template < typename TupleType >
600  inline void SetTuple(TupleType&, const boost::python::tuple&, SizeT<0>) { }
601 
604  template < typename TupleType, size_t N >
605  inline void SetTuple(TupleType& tuple, const boost::python::tuple& pyTuple, SizeT<N>)
606  {
607  SetTuple(tuple, pyTuple, SizeT < N - 1 > ());
608  std::get < N - 1 > (tuple) = convertPythonObject(pyTuple[N - 1], std::get < N - 1 > (tuple));
609  }
618  template<typename... Types>
619  std::tuple<Types...> convertPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>&)
620  {
621  std::tuple<Types...> tmpTuple;
622  const boost::python::tuple& pyTuple = static_cast<const boost::python::tuple&>(pyObject);
623  SetTuple(tmpTuple, pyTuple);
624  return tmpTuple;
625  }
626 
635  template <typename... Types>
636  inline void SetVariant(boost::variant<Types...>&, const boost::python::object& pyObject, SizeT<0>)
637  {
638  throw std::runtime_error(std::string("Could not set module parameter: Expected type '") +
639  Type<boost::variant<Types...> >::name() + "' instead of '" +
640  pyObject.ptr()->ob_type->tp_name + "'.");
641  }
642 
644  template < typename... Types, size_t N >
645  inline void SetVariant(boost::variant<Types...>& variant, const boost::python::object& pyObject, SizeT<N>)
646  {
647  using Scalar = typename std::tuple_element < N - 1, std::tuple<Types...> >::type;
648  if (checkPythonObject(pyObject, Scalar())) {
649  try {
650  Scalar value = convertPythonObject(pyObject, Scalar());
651  variant = value;
652  return;
653  } catch (std::runtime_error& e) {
654  }
655  }
656  // Conversion failed - try next type
657  SetVariant(variant, pyObject, SizeT < N - 1 > ());
658  }
667  template<typename... Types>
668  boost::variant<Types...> convertPythonObject(const boost::python::object& pyObject, const boost::variant<Types...>&)
669  {
670  boost::variant<Types...> tmpVariant;
671  SetVariant(tmpVariant, pyObject, SizeT<sizeof...(Types)>());
672  return tmpVariant;
673  }
674 
681  template<typename Type>
682  boost::optional<Type> convertPythonObject(const boost::python::object& pyObject, const boost::optional<Type>&)
683  {
684  boost::optional<Type> tmpOptional = boost::none;
685  if (!pyObject.is_none()) {
686  tmpOptional = convertPythonObject(pyObject, Type());
687  }
688  return tmpOptional;
689  }
690  }
692 }
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 ---------------------—
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 SetTuple(TupleType &tuple, const boost::python::tuple &pyTuple)
TMP (Template Meta Programming ) The given python tuple is written into the given c++ tuple.
Scalar convertPythonObject(const boost::python::object &pyObject, Scalar)
Convert from Python to given type.
bool CheckVariant(const VariantType &, const boost::python::object &, SizeT< 0 >)
Recursion sentinal for the case that all former type checks failed for the variant.
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.
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.
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 ...
Abstract base class for different kinds of events.
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.