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 {
49namespace python {
50
51// Prevent a converter from being registered multiple times.
53 public:
54 static bool get(const std::string& name);
55 static void set(const std::string& name);
56
57 private:
58 static std::map<std::string, bool> _registry;
59};
60
61// Check if the given object is a sequence object.
62// If so, return true.
63// py_obj gets changed if the given object was a scalar numpy/numarray.
64// In that case it is flattened.
65bool getSeqObject(boost::python::object& py_obj);
66
67// Convert a String object to python.
69 static boost::python::object makeobject(String const& s) {
70 return boost::python::object((const std::string&)s);
71 }
72 static PyObject* convert(String const& s) { return boost::python::incref(makeobject(s).ptr()); }
73};
74
75// Convert a String object from python.
78 boost::python::converter::registry::push_back(&convertible, &construct,
79 boost::python::type_id<String>());
80 }
81
82 static void* convertible(PyObject* obj_ptr) {
83#ifdef IS_PY3K
84 if (!PyUnicode_Check(obj_ptr)) return 0;
85#else
86 if (!PyString_Check(obj_ptr) && !PyUnicode_Check(obj_ptr)) return 0;
87#endif
88 return obj_ptr;
89 }
90
91 static void construct(PyObject* obj_ptr,
92 boost::python::converter::rvalue_from_python_stage1_data* data) {
93 char* value = NULL;
94 boost::python::object temp_bytes_obj;
95 if (PyUnicode_Check(obj_ptr)) {
96 PyObject* temp_bytes =
97 PyUnicode_AsEncodedString(obj_ptr, "UTF-8", "strict"); // Owned reference
98
99 if (temp_bytes != NULL) {
100 // Take over lifetime management of temp_bytes
101 temp_bytes_obj = boost::python::object(boost::python::handle<>(temp_bytes));
102 value = PyBytes_AS_STRING(temp_bytes);
103 } else {
104 boost::python::throw_error_already_set();
105 }
106#ifndef IS_PY3K
107 } else if (PyString_Check(obj_ptr)) {
108 value = PyString_AsString(obj_ptr);
109#endif
110 } else {
111 boost::python::throw_error_already_set();
112 }
113 if (value == 0) boost::python::throw_error_already_set();
114 void* storage =
115 ((boost::python::converter::rvalue_from_python_storage<String>*)data)->storage.bytes;
116 new (storage) String(value);
117 data->convertible = storage;
118 }
119};
120
121// Default operations on all containers for conversion from Python
122// container to C++ one.
123
124// Copied from
125// scitbx/include/scitbx/boost_python/container_conversions.h that is
126// described in the <a
127// href="http://www.boost.org/libs/python/doc/v2/faq.html">
128// Boost.Python FAQ. </a>
129
130// @author Ralf W. Grosse-Kunstleve <rwgk@yahoo.com> of
131// <a href="http://www.lbl.gov/">Lawrence Berkeley National Laboratory</a>
133 static bool check_convertibility_per_element() { return true; }
134
135 template <typename ContainerType>
136 static bool check_size(boost::type<ContainerType>, std::size_t) {
137 return true;
138 }
139
140 template <typename ContainerType>
141 static void assert_size(boost::type<ContainerType>, std::size_t) {}
142
143 template <typename ContainerType>
144 static void reserve(ContainerType&, std::size_t) {}
145};
146
147// Operations on containers that have variable capacity for
148// conversion from Python container to C++ one.
149
150// Copied from
151// scitbx/include/scitbx/boost_python/container_conversions.h that is
152// described in the <a
153// href="http://www.boost.org/libs/python/doc/v2/faq.html">
154// Boost.Python FAQ. </a>
155
156// @author Ralf W. Grosse-Kunstleve <rwgk@yahoo.com> of
157// <a href="http://www.lbl.gov/">Lawrence Berkeley National Laboratory</a>
159 template <typename ContainerType>
160 static void reserve(ContainerType& a, std::size_t sz) {
161 a.reserve(sz);
162 }
163
164 template <typename ContainerType, typename ValueType>
165 static void set_value(ContainerType& a, std::size_t i, ValueType const& v) {
166 AlwaysAssert(a.size() == i, AipsError);
167 a.push_back(v);
168 }
169};
170
172 template <typename ContainerType>
173 static void reserve(ContainerType& a, std::size_t sz) {
174 a.resize(sz);
175 }
176
177 template <typename ContainerType, typename ValueType>
178 static void set_value(ContainerType& a, std::size_t i, ValueType const& v) {
179 assert(a.size() > i);
180 a[i] = v;
181 }
182};
183
185 template <typename ContainerType>
186 static void reserve(ContainerType& a, std::size_t sz) {
187 a.resize(sz);
188 }
189
190 template <typename ContainerType, typename ValueType>
191 static void set_value(ContainerType& a, std::size_t i, ValueType const& v) {
192 assert(a.size() > i);
193 a[a.size() - i - 1] = v;
194 }
195};
196
197// A wrapper of a conversion function to convert a STL vector to a
198// Python list. This class satisfies the requirements of the
199// boost::python::to_python_converter conversion template argument.
200
201// Copied from
202// scitbx/include/scitbx/boost_python/container_conversions.h that is
203// described in the <a
204// href="http://www.boost.org/libs/python/doc/v2/faq.html">
205// Boost.Python FAQ. </a>
206
207// @author Ralf W. Grosse-Kunstleve <rwgk@yahoo.com> of
208// <a href="http://www.lbl.gov/">Lawrence Berkeley National Laboratory</a>
209template <typename ContainerType>
210struct to_list {
211 // Creates and returns a Python list from the elements copied
212 // from a STL container. The ContainerType must be a container
213 // with STL iterators defined on it.
214 // It may contain any type of object supported by the
215 // boost::python::object constructor.
216 static boost::python::object makeobject(ContainerType const& c) {
217 boost::python::list result;
218 typename ContainerType::const_iterator i = c.begin();
219 typename ContainerType::const_iterator iEnd = c.end();
220 for (; i != iEnd; ++i) {
221 result.append(*i);
222 }
223 return result;
224 }
225 static PyObject* convert(ContainerType const& c) {
226 return boost::python::incref(makeobject(c).ptr());
227 }
228};
229// # Make specialisations for various types.
230template <>
233 static boost::python::list makeobject(ContainerType const& c) {
234 // Reverse IPosition values.
235 boost::python::list result;
236 for (int i = c.size() - 1; i >= 0; --i) {
237 result.append(c[i]);
238 }
239 return result;
240 }
241 static PyObject* convert(ContainerType const& c) {
242 return boost::python::incref(makeobject(c).ptr());
243 }
244};
245// # This specialisation is needed because on OS-X 10.9 clang-3.5 with
246// # Boost-Python 1.57 gives a compile error
247// # /opt/casa/01/include/boost/python/converter/arg_to_python.hpp:209:9:
248// # error: no matching constructor for initialization of
249// # 'boost::python::converter::detail::arg_to_python_base'
250// # : arg_to_python_base(&x, registered<T>::converters)
251template <>
252struct to_list<std::vector<bool>> {
253 typedef std::vector<bool> ContainerType;
254 static boost::python::list makeobject(ContainerType const& c) {
255 boost::python::list result;
256 ContainerType::const_iterator i = c.begin();
257 ContainerType::const_iterator iEnd = c.end();
258 for (; i != iEnd; ++i) {
259 bool b = *i;
260 result.append(b);
261 }
262 return result;
263 }
264 static PyObject* convert(ContainerType const& c) {
265 return boost::python::incref(makeobject(c).ptr());
266 }
267};
268template <>
269struct to_list<std::vector<casacore::String>> {
270 typedef std::vector<casacore::String> ContainerType;
271 static boost::python::list makeobject(ContainerType const& c) {
272 boost::python::list result;
273 ContainerType::const_iterator i = c.begin();
274 ContainerType::const_iterator iEnd = c.end();
275 for (; i != iEnd; ++i) {
276 result.append((std::string const&)(*i));
277 }
278 return result;
279 }
280 static PyObject* convert(ContainerType const& c) {
281 return boost::python::incref(makeobject(c).ptr());
282 }
283};
284template <>
285struct to_list<casacore::Array<casacore::String>> {
287 static boost::python::object makeobject(ContainerType const& c) {
288 boost::python::list result;
289 ContainerType::const_iterator i = c.begin();
290 ContainerType::const_iterator iEnd = c.end();
291 for (; i != iEnd; ++i) {
292 result.append((std::string const&)(*i));
293 }
294 return result;
295 }
296 static PyObject* convert(ContainerType const& c) {
297 return boost::python::incref(makeobject(c).ptr());
298 }
299};
300template <>
301struct to_list<casacore::Vector<casacore::String>> {
303 static boost::python::object makeobject(ContainerType const& c) {
304 boost::python::list result;
305 ContainerType::const_iterator i = c.begin();
306 ContainerType::const_iterator iEnd = c.end();
307 for (; i != iEnd; ++i) {
308 result.append((std::string const&)(*i));
309 }
310 return result;
311 }
312 static PyObject* convert(ContainerType const& c) {
313 return boost::python::incref(makeobject(c).ptr());
314 }
315};
316
317// Converts an STL vector or casa Array of T objects to Python list.
318// Copied from
319// scitbx/include/scitbx/boost_python/container_conversions.h that is
320// described in the <a
321// href="http://www.boost.org/libs/python/doc/v2/faq.html">
322// Boost.Python FAQ. </a>
323// @author Ralf W. Grosse-Kunstleve <rwgk@yahoo.com> of
324// <a href="http://www.lbl.gov/">Lawrence Berkeley National Laboratory</a>
325template <typename T>
328 boost::python::to_python_converter<std::vector<T>, to_list<std::vector<T>>>();
329 }
330};
331template <typename T>
334 boost::python::to_python_converter<casacore::Array<T>, to_list<casacore::Array<T>>>();
335 }
336};
337template <typename T>
340 boost::python::to_python_converter<casacore::Vector<T>, to_list<casacore::Vector<T>>>();
341 }
342};
345 boost::python::to_python_converter<casacore::IPosition, to_list<casacore::IPosition>>();
346 }
347};
348
349// Conversion of Python sequence to C++ container.
350
351// Copied from
352// scitbx/include/scitbx/boost_python/container_conversions.h that is
353// described in the <a
354// href="http://www.boost.org/libs/python/doc/v2/faq.html">
355// Boost.Python FAQ. </a>
356// @author Ralf W. Grosse-Kunstleve <rwgk@yahoo.com> of
357// <a href="http://www.lbl.gov/">Lawrence Berkeley National Laboratory</a>
358template <typename ContainerType, typename ConversionPolicy>
360 typedef typename ContainerType::value_type container_element_type;
361
363 boost::python::converter::registry::push_back(&convertible, &construct,
364 boost::python::type_id<ContainerType>());
365 }
366
367 // Appears to return @a obj_ptr if it is type of Python sequence
368 // that can be convertible to C++ container.
369 static void* convertible(PyObject* obj_ptr) {
370 using namespace boost::python;
371 handle<> py_hdl(obj_ptr);
372 if (PyErr_Occurred()) {
373 PyErr_Clear();
374 return 0;
375 }
376 object py_obj(py_hdl);
377 incref(obj_ptr); // incr refcount, because ~object decrements it
378 // Accept single values.
379 if (PyBool_Check(obj_ptr) || PyLong_Check(obj_ptr) || PyFloat_Check(obj_ptr) ||
380 PyComplex_Check(obj_ptr)
381#ifndef IS_PY3K
382 || PyInt_Check(obj_ptr) || PyString_Check(obj_ptr)
383#endif
384 || PyUnicode_Check(obj_ptr)) {
385 extract<container_element_type> elem_proxy(py_obj);
386 if (!elem_proxy.check()) return 0;
387 return obj_ptr;
388 }
389 // An array scalar is accepted.
390 if (PycArrayScalarCheck(obj_ptr)) {
391 return obj_ptr;
392 }
393 // Get the sequence object.
394 // It can be a numarray/numpy scalar in which case
395 // it fills py_obj with a flattened array.
396 if (!getSeqObject(py_obj)) {
397 return 0;
398 }
399 // Check the sequence.
400 // It must be convertible to an iterator.
401 handle<> obj_iter(allow_null(PyObject_GetIter(py_obj.ptr())));
402 if (!obj_iter.get()) {
403 PyErr_Clear();
404 return 0;
405 }
406 if (!check_convertibility(py_obj.ptr())) {
407 return 0;
408 }
409 return obj_ptr;
410 }
411
412 // Constructs a C++ container from a Python sequence.
413 static void construct(PyObject* obj_ptr,
414 boost::python::converter::rvalue_from_python_stage1_data* data) {
415 using namespace boost::python;
416 using boost::python::converter::rvalue_from_python_storage;
417 void* storage = ((rvalue_from_python_storage<ContainerType>*)data)->storage.bytes;
418 new (storage) ContainerType();
419 data->convertible = storage;
420 ContainerType& result = *((ContainerType*)storage);
421 if (PyBool_Check(obj_ptr) || PyLong_Check(obj_ptr) || PyFloat_Check(obj_ptr) ||
422 PyComplex_Check(obj_ptr) || PyUnicode_Check(obj_ptr)
423#ifndef IS_PY3K
424 || PyString_Check(obj_ptr) || PyInt_Check(obj_ptr)
425#endif
426 || PycArrayScalarCheck(obj_ptr)) {
427 extract<container_element_type> elem_proxy(obj_ptr);
428 ConversionPolicy::reserve(result, 1);
429 ConversionPolicy::set_value(result, 0, elem_proxy());
430 return;
431 }
432 handle<> py_hdl(obj_ptr);
433 object py_obj = object(py_hdl);
434 incref(obj_ptr); // incr refcount, because ~object decrements it
435 assert(getSeqObject(py_obj));
436 fill_container(result, py_obj.ptr());
437 // ConversionPolicy::reserve(result, 1);
438 // ConversionPolicy::set_value(result, 0,
439 // extract<container_element_type>(py_flat.attr("__getitem__")(0)));
440 }
441
442 // Constructs a C++ container from a Python sequence.
443 static ContainerType make_container(PyObject* obj_ptr) {
444 ContainerType result;
445 fill_container(result, obj_ptr);
446 return result;
447 }
448
449 private:
450 static void fill_container(ContainerType& result, PyObject* obj_ptr) {
451 using namespace boost::python;
452 int obj_size = PyObject_Length(obj_ptr);
453 handle<> obj_iter(PyObject_GetIter(obj_ptr));
454 ConversionPolicy::reserve(result, obj_size);
455 std::size_t i = 0;
456 for (;; i++) {
457 handle<> py_elem_hdl(allow_null(PyIter_Next(obj_iter.get())));
458 if (PyErr_Occurred()) throw_error_already_set();
459 if (!py_elem_hdl.get()) break; // end of iteration
460 object py_elem_obj(py_elem_hdl);
461 extract<container_element_type> elem_proxy(py_elem_obj);
462 ConversionPolicy::set_value(result, i, elem_proxy());
463 }
464 ConversionPolicy::assert_size(boost::type<ContainerType>(), i);
465 }
466
467 static bool check_convertibility(PyObject* obj_ptr) {
468 using namespace boost::python;
469 handle<> obj_iter(allow_null(PyObject_GetIter(obj_ptr)));
470 if (!obj_iter.get()) { // must be convertible to an iterator
471 PyErr_Clear();
472 return false;
473 }
474 int obj_size = PyObject_Length(obj_ptr);
475 if (obj_size < 0) { // must be a measurable sequence
476 PyErr_Clear();
477 return false;
478 }
479 if (ConversionPolicy::check_convertibility_per_element()) {
480 if (!ConversionPolicy::check_size(boost::type<ContainerType>(), obj_size)) return false;
481 // All elements in a range and array have the same type, so
482 // need to check the first element only.
483 bool is_same = PyRange_Check(obj_ptr) || (PySequence_Check(obj_ptr) &&
484 !PyTuple_Check(obj_ptr) && !PyList_Check(obj_ptr));
485 [[maybe_unused]] int i = 0;
486 while (true) {
487 handle<> py_elem_hdl(allow_null(PyIter_Next(obj_iter.get())));
488 if (PyErr_Occurred()) {
489 PyErr_Clear();
490 return false;
491 }
492 if (!py_elem_hdl.get()) break; // end of iteration
493 object py_elem_obj(py_elem_hdl);
494 extract<container_element_type> elem_proxy(py_elem_obj);
495 if (!elem_proxy.check()) return false;
496 if (is_same) break; // all elements are of the same type
497 ++i;
498 }
499 if (!is_same) assert(i == obj_size);
500 }
501 return true;
502 }
503};
504
505// Register the String conversion.
507 static void reg();
508};
510
511// Register the IPosition conversion.
513 static void reg();
514};
516
517// Register the std::vector conversions.
518template <typename T>
520 static void reg() {
521 std::string tname(typeid(std::vector<T>).name());
522 if (!pyregistry::get(tname)) {
523 pyregistry::set(tname);
526 }
527 }
528};
529template <typename T>
533
534// Register the casacore::Vector conversions.
535template <typename T>
537 static void reg() {
538 std::string tname(typeid(casacore::Vector<T>).name());
539 if (!pyregistry::get(tname)) {
540 pyregistry::set(tname);
544 }
545 }
546};
547template <typename T>
551
552// Register all standard basic conversions.
553// These are String, IPosition, Vector<String>, Vector<Int>,
554// Vector<Double>, Vector<DComplex>.
556} // namespace python
557} // namespace casacore
558
559#endif
#define AlwaysAssert(expr, exception)
These marcos are provided for use instead of simply using the assert_ function directly.
Definition Assert.h:133
ConstIteratorSTL const_iterator
Definition Array.h:808
String: the storage and methods of handling collections of characters.
Definition String.h:355
Prevent a converter from being registered multiple times.
static std::map< std::string, bool > _registry
static bool get(const std::string &name)
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()
For temporary backward namespace compatibility, use casa as alias for casacore.
Definition mainpage.dox:28
T * storage()
If you really, really, need a "raw" pointer to the beginning of the storage area this will give it to...
Definition Block.h:559
String name() const
Return the name of the field.
NewDelAllocator< T > NewDelAllocator< T >::value
Definition Allocator.h:360
Define real & complex conjugation for non-complex types and put comparisons into std namespace.
Definition Complex.h:344
static void set_value(ContainerType &a, std::size_t i, ValueType const &v)
static void reserve(ContainerType &a, std::size_t sz)
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.