casacore
Loading...
Searching...
No Matches
PycBasicData.h
Go to the documentation of this file.
1//# PycBasicData.h: Convert casa data types to/from python
2//# Copyright (C) 2006
3//# Associated Universities, Inc. Washington DC, USA.
4//#
5//# This library is free software; you can redistribute it and/or modify it
6//# under the terms of the GNU Library General Public License as published by
7//# the Free Software Foundation; either version 2 of the License, or (at your
8//# option) any later version.
9//#
10//# This library is distributed in the hope that it will be useful, but WITHOUT
11//# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12//# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13//# License for more details.
14//#
15//# You should have received a copy of the GNU Library General Public License
16//# along with this library; if not, write to the Free Software Foundation,
17//# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
18//#
19//# Correspondence concerning AIPS++ should be addressed as follows:
20//# Internet email: casa-feedback@nrao.edu.
21//# Postal address: AIPS++ Project Office
22//# National Radio Astronomy Observatory
23//# 520 Edgemont Road
24//# Charlottesville, VA 22903-2475 USA
25
26#ifndef PYRAP_PYCBASICDATA_H
27#define PYRAP_PYCBASICDATA_H
28
29// include python first to avoid _POSIX_C_SOURCE redefined warnings
30#include <boost/python.hpp>
31#include <boost/python/object.hpp>
32#include <casacore/python/Converters/PycArray.h>
33#include <casacore/casa/BasicSL/String.h>
34#include <casacore/casa/Arrays/IPosition.h>
35#include <casacore/casa/Arrays/Vector.h>
36#include <casacore/casa/Utilities/Assert.h>
37#include <casacore/casa/Exceptions/Error.h>
38#include <vector>
39#include <map>
40
41#if PY_MAJOR_VERSION >= 3
42#define IS_PY3K
43#endif
44
45// Define classes and functions to convert the basic data types and
46// containers to and from Python.
47
48namespace casacore { namespace python {
49
50 // Prevent a converter from being registered multiple times.
52 {
53 public:
54 static bool get (const std::string& name);
55 static void set (const std::string& name);
56 private:
57 static std::map<std::string,bool> _registry;
58 };
59
60 // Check if the given object is a sequence object.
61 // If so, return true.
62 // py_obj gets changed if the given object was a scalar numpy/numarray.
63 // In that case it is flattened.
64 bool getSeqObject (boost::python::object& py_obj);
65
66 // Convert a String object to python.
68 {
69 static boost::python::object makeobject(String const& s)
70 { return boost::python::object((const std::string&)s); }
71 static PyObject* convert(String const& s)
72 { return boost::python::incref(makeobject(s).ptr()); }
73 };
74
75 // Convert a String object from python.
77 {
79 {
80 boost::python::converter::registry::push_back(
82 &construct,
83 boost::python::type_id<String>());
84 }
85
86 static void* convertible(PyObject* obj_ptr)
87 {
88#ifdef IS_PY3K
89 if (!PyUnicode_Check(obj_ptr)) return 0;
90#else
91 if (!PyString_Check(obj_ptr) && !PyUnicode_Check(obj_ptr)) return 0;
92#endif
93 return obj_ptr;
94 }
95
96 static void construct(
97 PyObject* obj_ptr,
98 boost::python::converter::rvalue_from_python_stage1_data* data)
99 {
100 char* value = NULL;
101 boost::python::object temp_bytes_obj;
102 if (PyUnicode_Check(obj_ptr)) {
103 PyObject * temp_bytes = PyUnicode_AsEncodedString(obj_ptr, "UTF-8", "strict"); // Owned reference
104
105 if (temp_bytes != NULL) {
106 // Take over lifetime management of temp_bytes
107 temp_bytes_obj = boost::python::object(boost::python::handle<>(temp_bytes));
108 value = PyBytes_AS_STRING(temp_bytes);
109 } else {
110 boost::python::throw_error_already_set();
111 }
112#ifndef IS_PY3K
113 } else if (PyString_Check(obj_ptr)) {
114 value = PyString_AsString(obj_ptr);
115#endif
116 } else {
117 boost::python::throw_error_already_set();
118 }
119 if (value == 0) boost::python::throw_error_already_set();
120 void* storage = (
121 (boost::python::converter::rvalue_from_python_storage<String>*)
122 data)->storage.bytes;
123 new (storage) String(value);
124 data->convertible = storage;
125 }
126 };
127
128
129 // Default operations on all containers for conversion from Python
130 // container to C++ one.
131
132 // Copied from
133 // scitbx/include/scitbx/boost_python/container_conversions.h that is
134 // described in the <a
135 // href="http://www.boost.org/libs/python/doc/v2/faq.html">
136 // Boost.Python FAQ. </a>
137
138 // @author Ralf W. Grosse-Kunstleve <rwgk@yahoo.com> of
139 // <a href="http://www.lbl.gov/">Lawrence Berkeley National Laboratory</a>
141 {
142 static bool check_convertibility_per_element() { return true; }
143
144 template <typename ContainerType>
145 static bool check_size(boost::type<ContainerType>, std::size_t)
146 {
147 return true;
148 }
149
150 template <typename ContainerType>
151 static void assert_size(boost::type<ContainerType>, std::size_t)
152 {}
153
154 template <typename ContainerType>
155 static void reserve(ContainerType&, std::size_t)
156 {}
157 };
158
159 // Operations on containers that have variable capacity for
160 // conversion from Python container to C++ one.
161
162 // Copied from
163 // scitbx/include/scitbx/boost_python/container_conversions.h that is
164 // described in the <a
165 // href="http://www.boost.org/libs/python/doc/v2/faq.html">
166 // Boost.Python FAQ. </a>
167
168 // @author Ralf W. Grosse-Kunstleve <rwgk@yahoo.com> of
169 // <a href="http://www.lbl.gov/">Lawrence Berkeley National Laboratory</a>
171 {
172 template <typename ContainerType>
173 static void reserve(ContainerType& a, std::size_t sz)
174 {
175 a.reserve(sz);
176 }
177
178 template <typename ContainerType, typename ValueType>
179 static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
180 {
181 AlwaysAssert(a.size() == i, AipsError);
182 a.push_back(v);
183 }
184 };
185
187 {
188 template <typename ContainerType>
189 static void reserve(ContainerType& a, std::size_t sz)
190 {
191 a.resize(sz);
192 }
193
194 template <typename ContainerType, typename ValueType>
195 static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
196 {
197 assert(a.size() > i);
198 a[i] = v;
199 }
200 };
201
203 {
204 template <typename ContainerType>
205 static void reserve(ContainerType& a, std::size_t sz)
206 {
207 a.resize(sz);
208 }
209
210 template <typename ContainerType, typename ValueType>
211 static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
212 {
213 assert(a.size() > i);
214 a[a.size() - i - 1] = v;
215 }
216 };
217
218
219 // A wrapper of a conversion function to convert a STL vector to a
220 // Python list. This class satisfies the requirements of the
221 // boost::python::to_python_converter conversion template argument.
222
223 // Copied from
224 // scitbx/include/scitbx/boost_python/container_conversions.h that is
225 // described in the <a
226 // href="http://www.boost.org/libs/python/doc/v2/faq.html">
227 // Boost.Python FAQ. </a>
228
229 // @author Ralf W. Grosse-Kunstleve <rwgk@yahoo.com> of
230 // <a href="http://www.lbl.gov/">Lawrence Berkeley National Laboratory</a>
231 template < typename ContainerType >
232 struct to_list
233 {
234 // Creates and returns a Python list from the elements copied
235 // from a STL container. The ContainerType must be a container
236 // with STL iterators defined on it.
237 // It may contain any type of object supported by the
238 // boost::python::object constructor.
239 static boost::python::object makeobject (ContainerType const& c)
240 {
241 boost::python::list result;
242 typename ContainerType::const_iterator i = c.begin();
243 typename ContainerType::const_iterator iEnd = c.end();
244 for( ; i != iEnd; ++i) {
245 result.append(*i);
246 }
247 return result;
248 }
249 static PyObject* convert (ContainerType const& c)
250 {
251 return boost::python::incref(makeobject(c).ptr());
252 }
253 };
254 //# Make specialisations for various types.
255 template <>
257 {
259 static boost::python::list makeobject (ContainerType const& c)
260 {
261 // Reverse IPosition values.
262 boost::python::list result;
263 for (int i=c.size()-1; i>=0; --i) {
264 result.append(c[i]);
265 }
266 return result;
267 }
268 static PyObject* convert (ContainerType const& c)
269 {
270 return boost::python::incref(makeobject(c).ptr());
271 }
272 };
273 //# This specialisation is needed because on OS-X 10.9 clang-3.5 with
274 //# Boost-Python 1.57 gives a compile error
275 //# /opt/casa/01/include/boost/python/converter/arg_to_python.hpp:209:9:
276 //# error: no matching constructor for initialization of
277 //# 'boost::python::converter::detail::arg_to_python_base'
278 //# : arg_to_python_base(&x, registered<T>::converters)
279 template <>
280 struct to_list <std::vector <bool> >
281 {
282 typedef std::vector <bool> ContainerType;
283 static boost::python::list makeobject (ContainerType const& c)
284 {
285 boost::python::list result;
286 ContainerType::const_iterator i = c.begin();
287 ContainerType::const_iterator iEnd = c.end();
288 for( ; i != iEnd; ++i) {
289 bool b = *i;
290 result.append(b);
291 }
292 return result;
293 }
294 static PyObject* convert (ContainerType const& c)
295 {
296 return boost::python::incref(makeobject(c).ptr());
297 }
298 };
299 template <>
300 struct to_list <std::vector <casacore::String> >
301 {
302 typedef std::vector <casacore::String> ContainerType;
303 static boost::python::list makeobject (ContainerType const& c)
304 {
305 boost::python::list result;
306 ContainerType::const_iterator i = c.begin();
307 ContainerType::const_iterator iEnd = c.end();
308 for( ; i != iEnd; ++i) {
309 result.append((std::string const&)(*i));
310 }
311 return result;
312 }
313 static PyObject* convert (ContainerType const& c)
314 {
315 return boost::python::incref(makeobject(c).ptr());
316 }
317 };
318 template <>
319 struct to_list <casacore::Array <casacore::String> >
320 {
321 typedef casacore::Array <casacore::String> ContainerType;
322 static boost::python::object makeobject (ContainerType const& c)
323 {
324 boost::python::list result;
325 ContainerType::const_iterator i = c.begin();
326 ContainerType::const_iterator iEnd = c.end();
327 for( ; i != iEnd; ++i) {
328 result.append((std::string const&)(*i));
329 }
330 return result;
331 }
332 static PyObject* convert (ContainerType const& c)
333 {
334 return boost::python::incref(makeobject(c).ptr());
335 }
336 };
337 template <>
338 struct to_list <casacore::Vector <casacore::String> >
339 {
340 typedef casacore::Vector <casacore::String> ContainerType;
341 static boost::python::object makeobject (ContainerType const& c)
342 {
343 boost::python::list result;
344 ContainerType::const_iterator i = c.begin();
345 ContainerType::const_iterator iEnd = c.end();
346 for( ; i != iEnd; ++i) {
347 result.append((std::string const&)(*i));
348 }
349 return result;
350 }
351 static PyObject* convert (ContainerType const& c)
352 {
353 return boost::python::incref(makeobject(c).ptr());
354 }
355 };
356
357 // Converts an STL vector or casa Array of T objects to Python list.
358 // Copied from
359 // scitbx/include/scitbx/boost_python/container_conversions.h that is
360 // described in the <a
361 // href="http://www.boost.org/libs/python/doc/v2/faq.html">
362 // Boost.Python FAQ. </a>
363 // @author Ralf W. Grosse-Kunstleve <rwgk@yahoo.com> of
364 // <a href="http://www.lbl.gov/">Lawrence Berkeley National Laboratory</a>
365 template < typename T >
367 {
369 {
370 boost::python::to_python_converter < std::vector < T >,
371 to_list < std::vector < T > > > ();
372 }
373 };
374 template < typename T >
376 {
378 {
379 boost::python::to_python_converter < casacore::Array < T >,
380 to_list < casacore::Array < T > > > ();
381 }
382 };
383 template < typename T >
385 {
387 {
388 boost::python::to_python_converter < casacore::Vector < T >,
389 to_list < casacore::Vector < T > > > ();
390 }
391 };
393 {
395 {
396 boost::python::to_python_converter < casacore::IPosition,
397 to_list < casacore::IPosition > > ();
398 }
399 };
400
401
402 // Conversion of Python sequence to C++ container.
403
404 // Copied from
405 // scitbx/include/scitbx/boost_python/container_conversions.h that is
406 // described in the <a
407 // href="http://www.boost.org/libs/python/doc/v2/faq.html">
408 // Boost.Python FAQ. </a>
409 // @author Ralf W. Grosse-Kunstleve <rwgk@yahoo.com> of
410 // <a href="http://www.lbl.gov/">Lawrence Berkeley National Laboratory</a>
411 template <typename ContainerType, typename ConversionPolicy>
413 {
414 typedef typename ContainerType::value_type container_element_type;
415
417 {
418 boost::python::converter::registry::push_back(
420 &construct,
421 boost::python::type_id<ContainerType>());
422 }
423
424 // Appears to return @a obj_ptr if it is type of Python sequence
425 // that can be convertible to C++ container.
426 static void* convertible(PyObject* obj_ptr)
427 {
428 using namespace boost::python;
429 handle<> py_hdl(obj_ptr);
430 if (PyErr_Occurred()) {
431 PyErr_Clear();
432 return 0;
433 }
434 object py_obj(py_hdl);
435 incref(obj_ptr); // incr refcount, because ~object decrements it
436 // Accept single values.
437 if (PyBool_Check(obj_ptr)
438 || PyLong_Check(obj_ptr)
439 || PyFloat_Check(obj_ptr)
440 || PyComplex_Check(obj_ptr)
441#ifndef IS_PY3K
442 || PyInt_Check(obj_ptr)
443 || PyString_Check(obj_ptr)
444#endif
445 || PyUnicode_Check(obj_ptr)) {
446 extract<container_element_type> elem_proxy(py_obj);
447 if (!elem_proxy.check()) return 0;
448 return obj_ptr;
449 }
450 // An array scalar is accepted.
451 if (PycArrayScalarCheck(obj_ptr)) {
452 return obj_ptr;
453 }
454 // Get the sequence object.
455 // It can be a numarray/numpy scalar in which case
456 // it fills py_obj with a flattened array.
457 if (! getSeqObject (py_obj)) {
458 return 0;
459 }
460 // Check the sequence.
461 // It must be convertible to an iterator.
462 handle<> obj_iter(allow_null(PyObject_GetIter(py_obj.ptr())));
463 if (!obj_iter.get()) {
464 PyErr_Clear();
465 return 0;
466 }
467 if (!check_convertibility (py_obj.ptr())) {
468 return 0;
469 }
470 return obj_ptr;
471 }
472
473 // Constructs a C++ container from a Python sequence.
474 static void construct(
475 PyObject* obj_ptr,
476 boost::python::converter::rvalue_from_python_stage1_data* data)
477 {
478 using namespace boost::python;
479 using boost::python::converter::rvalue_from_python_storage;
480 void* storage = (
481 (rvalue_from_python_storage<ContainerType>*)
482 data)->storage.bytes;
483 new (storage) ContainerType();
484 data->convertible = storage;
485 ContainerType& result = *((ContainerType*)storage);
486 if (PyBool_Check(obj_ptr)
487 || PyLong_Check(obj_ptr)
488 || PyFloat_Check(obj_ptr)
489 || PyComplex_Check(obj_ptr)
490 || PyUnicode_Check(obj_ptr)
491#ifndef IS_PY3K
492 || PyString_Check(obj_ptr)
493 || PyInt_Check(obj_ptr)
494#endif
495 || PycArrayScalarCheck(obj_ptr)) {
496 extract<container_element_type> elem_proxy(obj_ptr);
497 ConversionPolicy::reserve(result, 1);
498 ConversionPolicy::set_value(result, 0, elem_proxy());
499 return;
500 }
501 handle<> py_hdl(obj_ptr);
502 object py_obj = object(py_hdl);
503 incref(obj_ptr); // incr refcount, because ~object decrements it
504 assert (getSeqObject (py_obj));
505 fill_container (result, py_obj.ptr());
506 // ConversionPolicy::reserve(result, 1);
507 // ConversionPolicy::set_value(result, 0,
508 // extract<container_element_type>(py_flat.attr("__getitem__")(0)));
509 }
510
511 // Constructs a C++ container from a Python sequence.
512 static ContainerType make_container(PyObject* obj_ptr)
513 {
514 ContainerType result;
515 fill_container (result, obj_ptr);
516 return result;
517 }
518
519 private:
520 static void fill_container (ContainerType& result, PyObject* obj_ptr)
521 {
522 using namespace boost::python;
523 int obj_size = PyObject_Length(obj_ptr);
524 handle<> obj_iter(PyObject_GetIter(obj_ptr));
525 ConversionPolicy::reserve(result, obj_size);
526 std::size_t i=0;
527 for(;;i++) {
528 handle<> py_elem_hdl(allow_null(PyIter_Next(obj_iter.get())));
529 if (PyErr_Occurred()) throw_error_already_set();
530 if (!py_elem_hdl.get()) break; // end of iteration
531 object py_elem_obj(py_elem_hdl);
532 extract<container_element_type> elem_proxy(py_elem_obj);
533 ConversionPolicy::set_value(result, i, elem_proxy());
534 }
535 ConversionPolicy::assert_size(boost::type<ContainerType>(), i);
536 }
537
538 static bool check_convertibility(PyObject* obj_ptr)
539 {
540 using namespace boost::python;
541 handle<> obj_iter(allow_null(PyObject_GetIter(obj_ptr)));
542 if (!obj_iter.get()) { // must be convertible to an iterator
543 PyErr_Clear();
544 return false;
545 }
546 int obj_size = PyObject_Length(obj_ptr);
547 if (obj_size < 0) { // must be a measurable sequence
548 PyErr_Clear();
549 return false;
550 }
551 if (ConversionPolicy::check_convertibility_per_element()) {
552 if (!ConversionPolicy::check_size(
553 boost::type<ContainerType>(), obj_size)) return false;
554 // All elements in a range and array have the same type, so
555 // need to check the first element only.
556 bool is_same = PyRange_Check(obj_ptr) ||
557 (PySequence_Check(obj_ptr)
558 && !PyTuple_Check(obj_ptr) && !PyList_Check(obj_ptr));
559 int i = 0;
560 for (;;i++) {
561 handle<> py_elem_hdl(allow_null(PyIter_Next(obj_iter.get())));
562 if (PyErr_Occurred()) {
563 PyErr_Clear();
564 return false;
565 }
566 if (!py_elem_hdl.get()) break; // end of iteration
567 object py_elem_obj(py_elem_hdl);
568 extract<container_element_type> elem_proxy(py_elem_obj);
569 if (!elem_proxy.check()) return false;
570 if (is_same) break; // all elements are of the same type
571 }
572 if (!is_same) assert(i == obj_size );
573 }
574 return true;
575 }
576 };
577
578 // Register the String conversion.
580 {
581 static void reg();
582 };
585
586 // Register the IPosition conversion.
588 {
589 static void reg();
590 };
593
594 // Register the std::vector conversions.
595 template < typename T >
597 {
598 static void reg()
599 {
600 std::string tname(typeid(std::vector<T>).name());
601 if (! pyregistry::get (tname)) {
602 pyregistry::set (tname);
603 std_vector_to_list < T > ();
604 from_python_sequence < std::vector < T >,
606 }
607 }
608 };
609 template < typename T >
612
613 // Register the casacore::Vector conversions.
614 template < typename T >
616 {
617 static void reg()
618 {
619 std::string tname(typeid(casacore::Vector<T>).name());
620 if (! pyregistry::get (tname)) {
621 pyregistry::set (tname);
622 casa_array_to_list < T > ();
623 casa_vector_to_list < T > ();
624 from_python_sequence < casacore::Vector < T >,
626 }
627 }
628 };
629 template < typename T >
632
633
634 // Register all standard basic conversions.
635 // These are String, IPosition, Vector<String>, Vector<Int>,
636 // Vector<Double>, Vector<DComplex>.
638}}
639
640#endif
#define AlwaysAssert(expr, exception)
These marcos are provided for use instead of simply using the constructors of assert_ to allow additi...
Definition Assert.h:155
ConstIteratorSTL const_iterator
Definition Array.h:838
String: the storage and methods of handling collections of characters.
Definition String.h:223
Prevent a converter from being registered multiple times.
static bool get(const std::string &name)
static std::map< std::string, bool > _registry
static void set(const std::string &name)
void register_convert_casa_string()
void register_convert_basicdata()
Register all standard basic conversions.
Bool PycArrayScalarCheck(PyObject *obj_ptr)
Check if the PyObject is an array scalar object.
void register_convert_casa_iposition()
void register_convert_casa_vector()
bool getSeqObject(boost::python::object &py_obj)
Check if the given object is a sequence object.
void register_convert_std_vector()
this file contains all the compiler specific defines
Definition mainpage.dox:28
LatticeExprNode value(const LatticeExprNode &expr)
This function returns the value of the expression without a mask.
Define real & complex conjugation for non-complex types and put comparisons into std namespace.
Definition Complex.h:350
static void set_value(ContainerType &a, std::size_t i, ValueType const &v)
static void reserve(ContainerType &a, std::size_t sz)
Convert a String object from python.
static void * convertible(PyObject *obj_ptr)
static void construct(PyObject *obj_ptr, boost::python::converter::rvalue_from_python_stage1_data *data)
Convert a String object to python.
static PyObject * convert(String const &s)
static boost::python::object makeobject(String const &s)
static void reserve(ContainerType &a, std::size_t sz)
static void set_value(ContainerType &a, std::size_t i, ValueType const &v)
Register the IPosition conversion.
Register the String conversion.
Register the casacore::Vector conversions.
Register the std::vector conversions.
Default operations on all containers for conversion from Python container to C++ one.
static void assert_size(boost::type< ContainerType >, std::size_t)
static bool check_size(boost::type< ContainerType >, std::size_t)
static bool check_convertibility_per_element()
static void reserve(ContainerType &, std::size_t)
Conversion of Python sequence to C++ container.
static void construct(PyObject *obj_ptr, boost::python::converter::rvalue_from_python_stage1_data *data)
Constructs a C++ container from a Python sequence.
static ContainerType make_container(PyObject *obj_ptr)
Constructs a C++ container from a Python sequence.
static void fill_container(ContainerType &result, PyObject *obj_ptr)
static void * convertible(PyObject *obj_ptr)
Appears to return obj_ptr if it is type of Python sequence that can be convertible to C++ container.
static bool check_convertibility(PyObject *obj_ptr)
ContainerType::value_type container_element_type
Converts an STL vector or casa Array of T objects to Python list.
Operations on containers that have variable capacity for conversion from Python container to C++ one.
static void reserve(ContainerType &a, std::size_t sz)
static void set_value(ContainerType &a, std::size_t i, ValueType const &v)
static boost::python::object makeobject(ContainerType const &c)
static PyObject * convert(ContainerType const &c)
static boost::python::list makeobject(ContainerType const &c)
static boost::python::object makeobject(ContainerType const &c)
static PyObject * convert(ContainerType const &c)
static boost::python::list makeobject(ContainerType const &c)
static boost::python::list makeobject(ContainerType const &c)
static PyObject * convert(ContainerType const &c)
A wrapper of a conversion function to convert a STL vector to a Python list.
static PyObject * convert(ContainerType const &c)
static boost::python::object makeobject(ContainerType const &c)
Creates and returns a Python list from the elements copied from a STL container.