Belle II Software  release-05-02-19
PyObjConvUtils.h
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2013 - Belle II Collaboration *
4  * *
5  * Author: The Belle II Collaboration *
6  * Contributors: Thomas Keck *
7  * *
8  * This software is provided "as is" without any warranty. *
9  **************************************************************************/
10 
11 #pragma once
12 
13 #include <boost/python/object.hpp>
14 #include <boost/python/list.hpp>
15 #include <boost/python/tuple.hpp>
16 #include <boost/python/dict.hpp>
17 #include <boost/python/extract.hpp>
18 
19 #include <boost/variant.hpp>
20 #include <boost/optional.hpp>
21 
22 #include <set>
23 #include <map>
24 #include <string>
25 #include <vector>
26 
27 
28 namespace Belle2 {
33  class Path;
34 
43  namespace PyObjConvUtils {
44 
45  /*
46  * We need forward declarations here, because otherwise the compiler ends up calling
47  * the wrong function!
48  */
50  bool checkPythonObject(const boost::python::object& pyObject, bool);
52  bool checkPythonObject(const boost::python::object& pyObject, float);
54  bool checkPythonObject(const boost::python::object& pyObject, double);
56  bool checkPythonObject(const boost::python::object& pyObject, int);
58  bool checkPythonObject(const boost::python::object& pyObject, unsigned int);
60  bool checkPythonObject(const boost::python::object& pyObject, unsigned long int);
62  bool checkPythonObject(const boost::python::object& pyObject, const std::string&);
63  template<typename Key, typename Value>
64  bool checkPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>&);
66  template<typename Value>
67  bool checkPythonObject(const boost::python::object& pyObject, const std::vector<Value>&);
69  template<typename Value>
70  bool checkPythonObject(const boost::python::object& pyObject, const std::set<Value>&);
72  template<typename... Types>
73  bool checkPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>&);
75  template<typename... Types>
76  bool checkPythonObject(const boost::python::object& pyObject, const boost::variant<Types...>&);
78  template<typename Type>
79  bool checkPythonObject(const boost::python::object& pyObject, const boost::optional<Type>&);
80 
82  template<typename Scalar>
83  Scalar convertPythonObject(const boost::python::object& pyObject, Scalar);
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  boost::optional<Type> convertPythonObject(const boost::python::object& pyObject, const boost::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 boost::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<boost::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 
194  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  }
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 boost::optional<Type>&)
340  {
341  return pyObject.is_none() or checkPythonObject(pyObject, Type());
342  }
343 
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  }
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 boost::optional<Type>& optional)
483  {
484  if (optional) {
485  return convertToPythonObject(*optional);
486  } else {
487  return boost::python::object();
488  }
489  }
490 
501  template<typename Scalar>
502  Scalar convertPythonObject(const boost::python::object& pyObject, const Scalar)
503  {
504 
505  Scalar tmpValue;
506  boost::python::extract<Scalar> valueProxy(pyObject);
507  if (valueProxy.check()) {
508  tmpValue = static_cast<Scalar>(valueProxy);
509  } else {
510  throw std::runtime_error(std::string("Could not convert value: Expected type '") + Type<Scalar>::name() + "' instead of '" +
511  pyObject.ptr()->ob_type->tp_name + "'.");
512  }
513  return tmpValue;
514 
515  }
516 
524  template<typename Value>
525  std::vector<Value> convertPythonObject(const boost::python::object& pyObject, const std::vector<Value>&)
526  {
527 
528  std::vector<Value> tmpVector;
529  if (PyList_Check(pyObject.ptr()) or PyGen_Check(pyObject.ptr())) {
530  iteratePythonObject(pyObject, [&tmpVector](const boost::python::object & element) {
531  tmpVector.emplace_back(convertPythonObject(element, Value()));
532  return true;
533  });
534  } else {
535  tmpVector.emplace_back(convertPythonObject(pyObject, Value()));
536  }
537  return tmpVector;
538  }
539 
541  template<typename Value>
542  std::set<Value> convertPythonObject(const boost::python::object& pyObject, const std::set<Value>&)
543  {
544  std::set<Value> result;
545  if (PyAnySet_Check(pyObject.ptr())) {
546  iteratePythonObject(pyObject, [&result](const boost::python::object & element) {
547  result.emplace(convertPythonObject(element, Value()));
548  return true;
549  });
550  } else {
551  result.emplace(convertPythonObject(pyObject, Value()));
552  }
553  return result;
554  }
555 
556 
563  template<typename Key, typename Value>
564  std::map<Key, Value> convertPythonObject(const boost::python::object& pyObject, const std::map<Key, Value>&)
565  {
566  std::map<Key, Value> tmpMap;
567  const boost::python::dict& pyDict = static_cast<const boost::python::dict&>(pyObject);
568  boost::python::list keys = pyDict.keys();
569 
570  for (int i = 0; i < boost::python::len(keys); ++i) {
571  Key key = convertPythonObject(keys[boost::python::object(i)], Key());
572  Value value = convertPythonObject(pyDict[key], Value());
573  tmpMap.insert(std::pair<Key, Value>(key, value));
574  }
575  return tmpMap;
576  }
577 
578 
587  template < typename TupleType >
588  inline void SetTuple(TupleType& tuple, const boost::python::tuple& pyTuple)
589  {
590  static const unsigned N = std::tuple_size<TupleType>::value;
591  if ((unsigned)boost::python::len(pyTuple) != N) {
592  throw std::runtime_error(std::string("Given python tuple has length ") +
593  std::to_string(boost::python::len(pyTuple)) +
594  ", expected " + std::to_string(N));
595  }
596  SetTuple(tuple, pyTuple, SizeT<N>());
597  }
598 
600  template < typename TupleType >
601  inline void SetTuple(TupleType&, const boost::python::tuple&, SizeT<0>) { }
602 
605  template < typename TupleType, size_t N >
606  inline void SetTuple(TupleType& tuple, const boost::python::tuple& pyTuple, SizeT<N>)
607  {
608  SetTuple(tuple, pyTuple, SizeT < N - 1 > ());
609  std::get < N - 1 > (tuple) = convertPythonObject(pyTuple[N - 1], std::get < N - 1 > (tuple));
610  }
619  template<typename... Types>
620  std::tuple<Types...> convertPythonObject(const boost::python::object& pyObject, const std::tuple<Types...>&)
621  {
622  std::tuple<Types...> tmpTuple;
623  const boost::python::tuple& pyTuple = static_cast<const boost::python::tuple&>(pyObject);
624  SetTuple(tmpTuple, pyTuple);
625  return tmpTuple;
626  }
627 
636  template <typename... Types>
637  inline void SetVariant(boost::variant<Types...>&, const boost::python::object& pyObject, SizeT<0>)
638  {
639  throw std::runtime_error(std::string("Could not set module parameter: Expected type '") +
640  Type<boost::variant<Types...> >::name() + "' instead of '" +
641  pyObject.ptr()->ob_type->tp_name + "'.");
642  }
643 
645  template < typename... Types, size_t N >
646  inline void SetVariant(boost::variant<Types...>& variant, const boost::python::object& pyObject, SizeT<N>)
647  {
648  using Scalar = typename std::tuple_element < N - 1, std::tuple<Types...> >::type;
649  if (checkPythonObject(pyObject, Scalar())) {
650  try {
651  Scalar value = convertPythonObject(pyObject, Scalar());
652  variant = value;
653  return;
654  } catch (std::runtime_error& e) {
655  }
656  }
657  // Conversion failed - try next type
658  SetVariant(variant, pyObject, SizeT < N - 1 > ());
659  }
668  template<typename... Types>
669  boost::variant<Types...> convertPythonObject(const boost::python::object& pyObject, const boost::variant<Types...>&)
670  {
671  boost::variant<Types...> tmpVariant;
672  SetVariant(tmpVariant, pyObject, SizeT<sizeof...(Types)>());
673  return tmpVariant;
674  }
675 
682  template<typename Type>
683  boost::optional<Type> convertPythonObject(const boost::python::object& pyObject, const boost::optional<Type>&)
684  {
685  boost::optional<Type> tmpOptional = boost::none;
686  if (!pyObject.is_none()) {
687  tmpOptional = convertPythonObject(pyObject, Type());
688  }
689  return tmpOptional;
690  }
691  }
693 }
Belle2::PyObjConvUtils::VariadicType::name
static std::string name()
type name.
Definition: PyObjConvUtils.h:146
Belle2::PyObjConvUtils::convertToPythonObject
boost::python::object convertToPythonObject(const Scalar &value)
------------— From C++ TO Python Converter ---------------------—
Definition: PyObjConvUtils.h:363
Belle2::PyObjConvUtils::Type
Converts a template argument into a string for corresponding Python type.
Definition: PyObjConvUtils.h:128
Belle2::PyObjConvUtils::Type< double >::name
static std::string name()
type name.
Definition: PyObjConvUtils.h:175
Belle2::PyObjConvUtils::SizeT
Helper construct for TMP that provides an index at compile time to recurse through type lists.
Definition: PyObjConvUtils.h:196
Belle2::PyObjConvUtils::convertToPythonObjectVisitor
Helper function object to unpack a value from a variant to a python object.
Definition: PyObjConvUtils.h:460
Belle2::PyObjConvUtils::SetTuple
void SetTuple(TupleType &tuple, const boost::python::tuple &pyTuple)
TMP (Template Meta Programming ) The given python tuple is written into the given c++ tuple.
Definition: PyObjConvUtils.h:596
Belle2::PyObjConvUtils::Type< unsigned int >::name
static std::string name()
type name.
Definition: PyObjConvUtils.h:165
Belle2::PyObjConvUtils::Type< std::tuple< Types... > >::name
static std::string name()
type name.
Definition: PyObjConvUtils.h:183
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::PyObjConvUtils::CheckTuple
bool CheckTuple(const std::tuple< Types... > &tuple, const boost::python::tuple &pyTuple, std::index_sequence< Is... >)
Check if all tuple elements match.
Definition: PyObjConvUtils.h:299
Belle2::PyObjConvUtils::Type< int >::name
static std::string name()
type name.
Definition: PyObjConvUtils.h:169
Belle2::PyObjConvUtils::iteratePythonObject
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 ...
Definition: PyObjConvUtils.h:266
Belle2::PyObjConvUtils::SetVariant
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.
Definition: PyObjConvUtils.h:645
Belle2::PyObjConvUtils::checkPythonObject
bool checkPythonObject(const boost::python::object &pyObject, bool)
check if the python object can be converted to the given type.
Definition: PyObjConvUtils.h:203
Belle2::PyObjConvUtils::convertToPythonObjectVisitor::operator()
boost::python::object operator()(const T &value) const
actually convert a value.
Definition: PyObjConvUtils.h:464
Belle2::PyObjConvUtils::VariadicType
Recursively convert multiple types to type names (used for tuples).
Definition: PyObjConvUtils.h:140
Belle2::PyObjConvUtils::Type::name
static std::string name()
type name.
Definition: PyObjConvUtils.h:156
Belle2::PyObjConvUtils::GetTuple
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...
Definition: PyObjConvUtils.h:423
Belle2::PyObjConvUtils::CheckVariant
bool CheckVariant(const VariantType &, const boost::python::object &, SizeT< 0 >)
Recursion sentinal for the case that all former type checks failed for the variant.
Definition: PyObjConvUtils.h:316
Belle2::PyObjConvUtils::Type< unsigned long int >::name
static std::string name()
type name.
Definition: PyObjConvUtils.h:167
Belle2::PyObjConvUtils::convertPythonObject
Scalar convertPythonObject(const boost::python::object &pyObject, Scalar)
Convert from Python to given type.
Definition: PyObjConvUtils.h:510