# HG changeset patch # User Mike Miller # Date 1493854245 25200 # Node ID 3613ffbd52b26b9ea751d250aacbc63b6e8b0b3e # Parent 95c6ad0be828248e35d6d3afc59ea9490624c89b Overhaul implicit conversion of arguments and return values * oct-py-types.cc, oct-py-types.h (pytave::py_implicitly_convert_argument, pytave::py_implicitly_convert_return_value): New functions. * __py_struct_from_dict__.cc, oct-py-eval.cc, pycall.cc, pyeval.cc, pyexec.cc: Use them instead of legacy conversion functions. Add necessary #includes, remove #includes of legacy header files. * @pyobject/subsasgn.m, @pyobject/subsref.m: Change %!tests that depend on NumPy implicit conversion into %!xtests. * octave_to_python.cc, octave_to_python.h, python_to_octave.cc, python_to_octave.h: Delete, no longer used. * Makefile.am (COMMON_SOURCE_FILES, PYTAVE_HEADER_FILES): Remove the files. diff -r 95c6ad0be828 -r 3613ffbd52b2 @pyobject/subsasgn.m --- a/@pyobject/subsasgn.m Tue May 02 17:40:01 2017 -0700 +++ b/@pyobject/subsasgn.m Wed May 03 16:30:45 2017 -0700 @@ -114,7 +114,8 @@ %! assert (d{5.5}, 11) %! assert (d{5}, 12) -%!test +## Test that depends on implicit creation of NumPy arrays, do we want this? +%!xtest %! % 2D array indexing %! A = pyobject ([1.1 2 3; 4 5 6]); %! A{1, 1} = 10; diff -r 95c6ad0be828 -r 3613ffbd52b2 @pyobject/subsref.m --- a/@pyobject/subsref.m Tue May 02 17:40:01 2017 -0700 +++ b/@pyobject/subsref.m Wed May 03 16:30:45 2017 -0700 @@ -152,7 +152,8 @@ %! assert (b, 2) %! assert (char (c), "Octave") -%!test +## Test that depends on implicit creation of NumPy arrays, do we want this? +%!xtest %! % 2D array indexing %! A = pyobject ([1. 2.; 3. 4.]); %! assert (A{1, 1}, 1) diff -r 95c6ad0be828 -r 3613ffbd52b2 Makefile.am --- a/Makefile.am Tue May 02 17:40:01 2017 -0700 +++ b/Makefile.am Wed May 03 16:30:45 2017 -0700 @@ -29,12 +29,10 @@ COMMON_SOURCE_FILES = \ exceptions.cc \ - octave_to_python.cc \ oct-py-eval.cc \ oct-py-init.cc \ oct-py-types.cc \ - oct-py-util.cc \ - python_to_octave.cc + oct-py-util.cc DOC_FILES = \ INSTALL.md \ @@ -72,9 +70,7 @@ oct-py-init.h \ oct-py-object.h \ oct-py-types.h \ - oct-py-util.h \ - octave_to_python.h \ - python_to_octave.h + oct-py-util.h TST_FILES = $(addsuffix -tst,$(OCT_SOURCE_FILES)) diff -r 95c6ad0be828 -r 3613ffbd52b2 __py_struct_from_dict__.cc --- a/__py_struct_from_dict__.cc Tue May 02 17:40:01 2017 -0700 +++ b/__py_struct_from_dict__.cc Wed May 03 16:30:45 2017 -0700 @@ -32,7 +32,9 @@ #include "oct-py-object.h" #include "oct-py-types.h" #include "oct-py-util.h" -#include "octave_to_python.h" + +// FIXME: only here for exception types still used in this file +#include DEFUN_DLD (__py_class_name__, args, , "-*- texinfo -*-\n\ @@ -299,24 +301,12 @@ pytave::py_init (); - // FIXME: PyObject *obj = convert argument to Python (args(0)); - PyObject *obj = nullptr; - try - { - boost::python::object arg; - pytave::octvalue_to_pyobj (arg, args(0)); - obj = arg.ptr (); - Py_INCREF (obj); - } - catch (pytave::value_convert_exception const &) - { - } + pytave::python_object obj = pytave::py_implicitly_convert_argument (args(0)); if (! obj) error ("__py_objstore_put__: VALUE must be convertible to a Python value"); - uint64_t key = pytave::py_objstore_put (obj); - Py_DECREF (obj); + uint64_t key = pytave::py_objstore_put (obj.release ()); return ovl (octave_uint64 (key)); } diff -r 95c6ad0be828 -r 3613ffbd52b2 oct-py-eval.cc --- a/oct-py-eval.cc Tue May 02 17:40:01 2017 -0700 +++ b/oct-py-eval.cc Wed May 03 16:30:45 2017 -0700 @@ -32,7 +32,7 @@ #include "oct-py-eval.h" #include "oct-py-object.h" #include "oct-py-util.h" -#include "octave_to_python.h" +#include "oct-py-types.h" namespace pytave { @@ -63,17 +63,12 @@ for (int i = 0; i < args.length (); ++i) { - boost::python::object arg; - pytave::octvalue_to_pyobj (arg, args(i)); - PyObject *obj = arg.ptr (); + python_object obj = py_implicitly_convert_argument (args(i)); if (pytave::is_py_kwargs_argument (obj)) kwargs = pytave::update_py_dict (kwargs, obj); else - { - Py_INCREF (obj); - PyList_Append (args_list, obj); - } + PyList_Append (args_list, obj.release ()); } python_object args_tuple = PyList_AsTuple (args_list); diff -r 95c6ad0be828 -r 3613ffbd52b2 oct-py-types.cc --- a/oct-py-types.cc Tue May 02 17:40:01 2017 -0700 +++ b/oct-py-types.cc Wed May 03 16:30:45 2017 -0700 @@ -28,15 +28,16 @@ #include #include #include +#include #include "exceptions.h" #include "oct-py-eval.h" #include "oct-py-object.h" #include "oct-py-types.h" +#include "oct-py-util.h" -// FIXME: only here to bootstrap nested conversions needed in this file -#include "octave_to_python.h" -#include "python_to_octave.h" +// FIXME: only here for exception types still used in this file +#include namespace pytave { @@ -311,26 +312,6 @@ return 0; } - inline PyObject * - wrap_octvalue_to_pyobj (const octave_value& value) - { - boost::python::object obj; - octvalue_to_pyobj (obj, value); - PyObject *ptr = obj.ptr (); - Py_INCREF (ptr); - return ptr; - } - - inline octave_value - wrap_pyobj_to_octvalue (PyObject *obj) - { - boost::python::object objref - { boost::python::handle<> (boost::python::borrowed (obj)) }; - octave_value value; - pyobj_to_octvalue (value, objref); - return value; - } - octave_scalar_map extract_py_scalar_map (PyObject *obj) { @@ -353,7 +334,7 @@ "all keys in the dict must be strings"); std::string key = extract_py_str (py_key); - octave_value value = wrap_pyobj_to_octvalue (py_value); + octave_value value = py_implicitly_convert_return_value (py_value); map.setfield (key, value); } @@ -373,7 +354,7 @@ if (! key) octave_throw_bad_alloc (); - PyObject *item = wrap_octvalue_to_pyobj (map.contents (p)); + PyObject *item = py_implicitly_convert_argument (map.contents (p)); if (PyDict_SetItem (dict, key, item) < 0) throw boost::python::error_already_set (); @@ -464,7 +445,7 @@ for (octave_idx_type i = 0; i < size; ++i) { - PyObject *item = wrap_octvalue_to_pyobj (cell.xelem (i)); + PyObject *item = py_implicitly_convert_argument (cell.xelem (i)); PyTuple_SET_ITEM (tuple, i, item); } @@ -507,4 +488,45 @@ #endif } + PyObject * + py_implicitly_convert_argument (const octave_value& value) + { + if (value.is_object () && value.class_name () == "pyobject") + return pyobject_unwrap_object (value); + else if (value.is_string () && value.rows () > 1) + error ("unable to convert multirow char array to a Python object"); + else if (value.is_string ()) + return make_py_str (value.string_value ()); + else if (value.is_scalar_type ()) + return make_py_numeric_value (value); + else if (value.is_cell ()) + return make_py_tuple (value.cell_value ()); + else if (value.is_numeric_type () && value.ndims () == 2 + && (value.columns () <= 1 || value.rows () <= 1)) + return make_py_array (value); + else if (value.is_map () && value.numel () == 1) + return make_py_dict (value.scalar_map_value ()); + else + error ("unable to convert unhandled Octave type to a Python object"); + + return nullptr; + } + + octave_value + py_implicitly_convert_return_value (PyObject *obj) + { + if (PyBool_Check (obj)) + return octave_value {extract_py_bool (obj)}; +#if PY_VERSION_HEX < 0x03000000 + else if (PyInt_Check (obj)) + return octave_value {octave_int64 (extract_py_int64 (obj))}; +#endif + else if (PyComplex_Check (obj)) + return octave_value {extract_py_complex (obj)}; + else if (PyFloat_Check (obj)) + return octave_value {extract_py_float (obj)}; + else + return pyobject_wrap_object (obj); + } + } diff -r 95c6ad0be828 -r 3613ffbd52b2 oct-py-types.h --- a/oct-py-types.h Tue May 02 17:40:01 2017 -0700 +++ b/oct-py-types.h Wed May 03 16:30:45 2017 -0700 @@ -217,6 +217,52 @@ PyObject * make_py_str (const std::string& str); + //! Perform an implicit conversion of the given Octave @c value to a Python + //! argument. + //! + //! The following implicit type conversions are implemented by this function: + //! + //! @arg @c bool from Octave logical scalar, + //! @arg @c complex from Octave double or single precision complex scalar, + //! @arg @c float from Octave double or single precision scalar, + //! @arg @c int from any Octave integer-valued scalar, + //! @arg @c long from Octave @c uint32, @c int64, or @c uint64, and only if + //! running against Python 2, + //! @arg @c str from Octave string (@c char row vector), + //! @arg @c array.array from Octave numeric column or row vector, + //! @arg @c dict from Octave scalar map (consisting entirely of implicitly + //! convertible elements), + //! @arg @c tuple from Octave cell array (consisting entirely of implicitly + //! convertible elements). + //! + //! If @c value refers to a previously created Python object, then a reference + //! to the existing object is returned. + //! + //! Otherwise, a conversion error is raised. + //! + //! @param value Octave value + //! @return Python object + PyObject * + py_implicitly_convert_argument (const octave_value& value); + + //! Perform an implicit conversion of the given Python object to an Octave + //! return value. + //! + //! The following implicit type conversions are implemented by this function: + //! + //! @arg @c logical scalar from Python @c bool, + //! @arg @c complex @c double from Python @c complex, + //! @arg @c double from Python @c float, + //! @arg @c int64 from Python @c int, only if running against Python 2. + //! + //! Otherwise, @c obj is left unconverted, a reference is maintained to it, + //! and an Octave value containing that reference is returned. + //! + //! @param obj Python object + //! @return Octave value + octave_value + py_implicitly_convert_return_value (PyObject *obj); + } #endif diff -r 95c6ad0be828 -r 3613ffbd52b2 octave_to_python.cc --- a/octave_to_python.cc Tue May 02 17:40:01 2017 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,207 +0,0 @@ -/* - -Copyright (C) 2015-2016 Mike Miller -Copyright (C) 2008 David Grundberg, Håkan Fors Nilsson -Copyright (C) 2009 VZLU Prague - -This file is part of Pytave. - -Pytave is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation, either version 3 of the License, or (at your -option) any later version. - -Pytave is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received a copy of the GNU General Public License -along with Pytave; see the file COPYING. If not, see -. - -*/ - -#if defined (HAVE_CONFIG_H) -# include -#endif - -#include - -#include -#include -#include -#include - -#include -#include "arrayobjectdefs.h" -#include "exceptions.h" -#include "octave_to_python.h" -#include "oct-py-types.h" -#include "oct-py-util.h" - -namespace pytave -{ - - template - static void - copy_octarray_to_pyarrobj (PyArrayObject *pyarr, const OctaveBase& matrix, - const unsigned int matindex, - const unsigned int matstride, - const int dimension, const unsigned int offset) - { - unsigned char *ptr = (unsigned char*) PyArray_DATA (pyarr); - if (dimension == PyArray_NDIM (pyarr) - 1) - { - // Last dimension, base case - for (int i = 0; i < PyArray_DIM (pyarr, dimension); i++) - { - *(PythonPrimitive *)&ptr[offset + i*PyArray_STRIDE (pyarr, dimension)] - = matrix.elem (matindex + i*matstride); - } - } - else - { - for (int i = 0; i < PyArray_DIM (pyarr, dimension); i++) - { - copy_octarray_to_pyarrobj ( - pyarr, - matrix, - matindex + i*matstride, - matstride * PyArray_DIM (pyarr, dimension), - dimension + 1, - offset + i*PyArray_STRIDE (pyarr, dimension)); - } - } - } - - static PyArrayObject * - createPyArr (const dim_vector& dims, int pyarrtype) - { - int len = dims.length (); - npy_intp dimensions[len]; - for (int i = 0; i < dims.length (); i++) - dimensions[i] = dims(i); - - return (PyArrayObject *)PyArray_SimpleNew (len, dimensions, pyarrtype); - } - - template - static PyArrayObject * - create_array (const OctaveBase& octarr, int pyarraytype) - { - PyArrayObject *pyarr = createPyArr (octarr.dims (), pyarraytype); - try - { - copy_octarray_to_pyarrobj (pyarr, octarr, 0, 1, 0, 0); - } - catch (const value_convert_exception&) - { - Py_DECREF (pyarr); - throw; - } - return pyarr; - } - - static PyArrayObject * - octvalue_to_pyarrobj (const octave_value& matrix) - { - if (matrix.is_double_type ()) - if (matrix.is_complex_type ()) - return create_array (matrix.complex_array_value (), NPY_CDOUBLE); - else if (matrix.is_real_type ()) - return create_array (matrix.array_value (), NPY_DOUBLE); - else - throw value_convert_exception ("Unknown double matrix type"); - - if (matrix.is_single_type ()) - if (matrix.is_complex_type ()) - return create_array (matrix.float_complex_array_value (), NPY_CFLOAT); - else if (matrix.is_real_type ()) - return create_array (matrix.float_array_value (), NPY_FLOAT); - else - throw value_convert_exception ("Unknown float matrix type"); - - if (matrix.is_int8_type ()) - return create_array (matrix.int8_array_value (), NPY_INT8); - if (matrix.is_int16_type ()) - return create_array (matrix.int16_array_value (), NPY_INT16); - if (matrix.is_int32_type ()) - return create_array (matrix.int32_array_value (), NPY_INT32); - if (matrix.is_int64_type ()) - return create_array (matrix.int64_array_value (), NPY_INT64); - - if (matrix.is_uint8_type ()) - return create_array (matrix.uint8_array_value (), NPY_UINT8); - if (matrix.is_uint16_type ()) - return create_array (matrix.uint16_array_value (), NPY_UINT16); - if (matrix.is_uint32_type ()) - return create_array (matrix.uint32_array_value (), NPY_UINT32); - if (matrix.is_uint64_type ()) - return create_array (matrix.uint64_array_value (), NPY_UINT64); - - if (matrix.is_bool_type ()) - return create_array (matrix.bool_array_value (), NPY_BOOL); - if (matrix.is_string ()) - return create_array (matrix.char_array_value (), NPY_CHAR); - - throw value_convert_exception ("Octave matrix type not known, conversion not implemented"); - } - - static void - octvalue_to_pyarr (boost::python::object& py_object, - const octave_value& octvalue) - { - PyArrayObject *pyarr = octvalue_to_pyarrobj (octvalue); - py_object = boost::python::object - { boost::python::handle<> ((PyObject *)pyarr) }; - } - - void octvalue_to_pyobj (boost::python::object& py_object, - const octave_value& octvalue) - { - if (octvalue.is_undefined ()) - throw value_convert_exception ( - "Octave value `undefined'. Can not convert to a Python object"); - else if (octvalue.is_string () && octvalue.rows () > 1) - throw value_convert_exception ( - "Octave multirow char array cannot be converted to a Python object"); - else if (octvalue.is_string ()) - { - PyObject *obj = make_py_str (octvalue.string_value ()); - py_object = boost::python::object { boost::python::handle<> (obj) }; - } - else if (octvalue.is_scalar_type ()) - { - PyObject *obj = make_py_numeric_value (octvalue); - py_object = boost::python::object { boost::python::handle<> (obj) }; - } - else if (octvalue.is_cell ()) - { - PyObject *obj = make_py_tuple (octvalue.cell_value ()); - py_object = boost::python::object { boost::python::handle<> (obj) }; - } - else if (octvalue.is_numeric_type () && octvalue.ndims () == 2 - && (octvalue.columns () <= 1 || octvalue.rows () <= 1)) - { - PyObject *obj = make_py_array (octvalue); - py_object = boost::python::object { boost::python::handle<> (obj) }; - } - else if (octvalue.is_numeric_type () || octvalue.is_string () - || octvalue.is_bool_type ()) - octvalue_to_pyarr (py_object, octvalue); - else if (octvalue.is_map () && octvalue.numel () == 1) - { - PyObject *obj = make_py_dict (octvalue.scalar_map_value ()); - py_object = boost::python::object { boost::python::handle<> (obj) }; - } - else if (octvalue.is_object () && octvalue.class_name () == "pyobject") - { - PyObject *obj = pyobject_unwrap_object (octvalue); - py_object = boost::python::object { boost::python::handle<> (obj) }; - } - else - throw value_convert_exception ( - "Conversion from Octave value not implemented"); - } -} diff -r 95c6ad0be828 -r 3613ffbd52b2 octave_to_python.h --- a/octave_to_python.h Tue May 02 17:40:01 2017 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/* - -Copyright (C) 2015-2016 Mike Miller -Copyright (C) 2008 David Grundberg, Håkan Fors Nilsson - -This file is part of Pytave. - -Pytave is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation, either version 3 of the License, or (at your -option) any later version. - -Pytave is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received a copy of the GNU General Public License -along with Pytave; see the file COPYING. If not, see -. - -*/ - -/*! \file octave_to_python.h - \brief Conversion of data values from GNU Octave to Python - - The functions in this file define the translation of GNU Octave - values to Python objects using the Boost.Python library and - the Octave/C++ API. - - This project is currently derived from an earlier project called - Pytave that allowed Python to call Octave functions on an - embedded Octave interpreter. The bulk of the project is in the - code to convert between Octave and Python data types, so most of - that is reusable. As a side goal, we may continue to maintain the - Python wrapper around Octave and incorporate that into Octave as - well, so that Octave can provide its own native Python module. -*/ - - -#if ! defined (pytave_octave_to_python_h) -#define pytave_octave_to_python_h - -#include -#include - -//! Contains the functions used for conversion. -namespace pytave -{ - //! Conversion from any Octave value object to a Python object. - /*! - \param py_object a reference to boost::python::object to store the result of the conversion. - \param oct_value a constant reference to a octave_value that contains the object to be converted. - \see pyobj_to_octvalue - */ - void octvalue_to_pyobj (boost::python::object& py_object, - const octave_value& oct_value); -} - -#endif diff -r 95c6ad0be828 -r 3613ffbd52b2 pycall.cc --- a/pycall.cc Tue May 02 17:40:01 2017 -0700 +++ b/pycall.cc Wed May 03 16:30:45 2017 -0700 @@ -33,9 +33,8 @@ #include "oct-py-eval.h" #include "oct-py-init.h" #include "oct-py-object.h" +#include "oct-py-types.h" #include "oct-py-util.h" -#include "octave_to_python.h" -#include "python_to_octave.h" DEFUN_DLD (pycall, args, nargout, "-*- texinfo -*-\n\ @@ -110,16 +109,11 @@ } octave_value_list arglist = args.slice (1, nargin - 1); - PyObject *result = pytave::py_call_function (callable, arglist); - boost::python::object res { boost::python::handle<> (result) }; + pytave::python_object res = pytave::py_call_function (callable, arglist); // Ensure reasonable "ans" behaviour, consistent with Python's "_". if (nargout > 0 || ! res.is_none ()) - { - octave_value val; - pytave::pyobj_to_octvalue (val, res); - retval(0) = val; - } + retval(0) = pytave::py_implicitly_convert_return_value (res); } catch (pytave::object_convert_exception const &) { @@ -231,7 +225,7 @@ %!assert (pycall (pyeval ("lambda x: type(x) == type(2**64) and x == 0"), intmin ("uint64"))) %!assert (pycall (pyeval ("lambda x: type(x) == type(2**64) and x == 2**64-1"), intmax ("uint64"))) -%!error +%!error %! pyexec ("def intwrapper(x): return int(x)"); %! pycall ("intwrapper", ftp ()); diff -r 95c6ad0be828 -r 3613ffbd52b2 pyeval.cc --- a/pyeval.cc Tue May 02 17:40:01 2017 -0700 +++ b/pyeval.cc Wed May 03 16:30:45 2017 -0700 @@ -33,8 +33,9 @@ #include "exceptions.h" #include "oct-py-eval.h" #include "oct-py-init.h" +#include "oct-py-object.h" +#include "oct-py-types.h" #include "oct-py-util.h" -#include "python_to_octave.h" DEFUN_DLD (pyeval, args, nargout, "-*- texinfo -*-\n\ @@ -85,15 +86,10 @@ try { - PyObject *obj = pytave::py_eval_string (code, 0, local_namespace); - boost::python::object res { boost::python::handle<> (obj) }; + pytave::python_object res = pytave::py_eval_string (code, 0, local_namespace); if (nargout > 0 || ! res.is_none ()) - { - octave_value val; - pytave::pyobj_to_octvalue (val, res); - retval(0) = val; - } + retval(0) = pytave::py_implicitly_convert_return_value (res); } catch (pytave::object_convert_exception const &) { diff -r 95c6ad0be828 -r 3613ffbd52b2 pyexec.cc --- a/pyexec.cc Tue May 02 17:40:01 2017 -0700 +++ b/pyexec.cc Wed May 03 16:30:45 2017 -0700 @@ -33,7 +33,6 @@ #include "oct-py-eval.h" #include "oct-py-init.h" #include "oct-py-util.h" -#include "python_to_octave.h" DEFUN_DLD (pyexec, args, nargout, "-*- texinfo -*-\n\ diff -r 95c6ad0be828 -r 3613ffbd52b2 python_to_octave.cc --- a/python_to_octave.cc Tue May 02 17:40:01 2017 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,329 +0,0 @@ -/* - -Copyright (C) 2015-2016 Mike Miller -Copyright (C) 2008 David Grundberg, Håkan Fors Nilsson -Copyright (C) 2009 VZLU Prague - -This file is part of Pytave. - -Pytave is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation, either version 3 of the License, or (at your -option) any later version. - -Pytave is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received a copy of the GNU General Public License -along with Pytave; see the file COPYING. If not, see -. - -*/ - -#if defined (HAVE_CONFIG_H) -# include -#endif - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "arrayobjectdefs.h" -#include "exceptions.h" -#include "oct-py-types.h" -#include "oct-py-util.h" -#include "python_to_octave.h" - -namespace pytave -{ - template - static void - copy_pyarrobj_to_octarray (OctaveBase& matrix, PyArrayObject *pyarr, - const int unsigned matindex, - const unsigned int matstride, - const int dimension, const unsigned int offset) - { - unsigned char *ptr = (unsigned char*) PyArray_DATA (pyarr); - if (dimension == PyArray_NDIM (pyarr) - 1) - { - // Last dimension, base case - for (int i = 0; i < PyArray_DIM (pyarr, dimension); i++) - { - matrix.elem (matindex + i*matstride) - = *(PythonPrimitive*) - &ptr[offset + i*PyArray_STRIDE (pyarr, dimension)]; - } - } - else if (PyArray_NDIM (pyarr) == 0) - { - matrix.elem (0) = *(PythonPrimitive*) ptr; - } - else - { - for (int i = 0; i < PyArray_DIM (pyarr, dimension); i++) - { - copy_pyarrobj_to_octarray ( - matrix, - pyarr, - matindex + i*matstride, - matstride * PyArray_DIM (pyarr, dimension), - dimension + 1, - offset + i*PyArray_STRIDE (pyarr, dimension)); - } - } - } - - template - static void - copy_pyarrobj_to_octarray_dispatch (OctaveBase& matrix, - PyArrayObject *pyarr, - const boost::true_type&) - { - copy_pyarrobj_to_octarray (matrix, pyarr, 0, 1, 0, 0); - } - - template - static void - copy_pyarrobj_to_octarray_dispatch (OctaveBase& matrix, - PyArrayObject *pyarr, - const boost::false_type&) - { - assert (0); - } - - template class matching_type : public boost::false_type { }; - template class matching_type : public boost::true_type { }; - template class matching_type > : public boost::true_type { }; - template <> class matching_type : public boost::true_type { }; - template <> class matching_type : public boost::true_type { }; - template <> class matching_type : public boost::true_type { }; - - template - static void - copy_pyarrobj_to_octarray_dispatch (OctaveBase& matrix, - PyArrayObject *pyarr) - { - matching_type inst; - copy_pyarrobj_to_octarray_dispatch (matrix, pyarr, inst); - } - - template - static void - copy_pyarrobj_to_octarray_boot (OctaveBase& matrix, PyArrayObject *pyarr) - { - -#define ARRAYCASE(AC_pyarrtype, AC_primitive) case AC_pyarrtype: \ - copy_pyarrobj_to_octarray_dispatch \ - (matrix, pyarr); \ - break; \ - - // Coerce NumPy's long type into one of two possible sized integer types - int type_num = PyArray_TYPE (pyarr); - switch (type_num) - { - case NPY_LONG: - if (sizeof (npy_long) == sizeof (int64_t)) - type_num = NPY_INT64; - else if (sizeof (npy_long) == sizeof (int32_t)) - type_num = NPY_INT32; - break; - case NPY_LONGLONG: - if (sizeof (npy_longlong) == sizeof (int64_t)) - type_num = NPY_INT64; - else if (sizeof (npy_longlong) == sizeof (int32_t)) - type_num = NPY_INT32; - break; - case NPY_ULONG: - if (sizeof (npy_ulong) == sizeof (uint64_t)) - type_num = NPY_UINT64; - else if (sizeof (npy_ulong) == sizeof (uint32_t)) - type_num = NPY_UINT32; - break; - case NPY_ULONGLONG: - if (sizeof (npy_ulonglong) == sizeof (uint64_t)) - type_num = NPY_UINT64; - else if (sizeof (npy_ulonglong) == sizeof (uint32_t)) - type_num = NPY_UINT32; - break; - } - - switch (type_num) - { - ARRAYCASE (NPY_INT8, int8_t) - ARRAYCASE (NPY_INT16, int16_t) - ARRAYCASE (NPY_INT32, int32_t) - ARRAYCASE (NPY_INT64, int64_t) - ARRAYCASE (NPY_UINT8, uint8_t) - ARRAYCASE (NPY_UINT16, uint16_t) - ARRAYCASE (NPY_UINT32, uint32_t) - ARRAYCASE (NPY_UINT64, uint64_t) - ARRAYCASE (NPY_FLOAT, float) - ARRAYCASE (NPY_DOUBLE, double) - ARRAYCASE (NPY_CFLOAT, FloatComplex) - ARRAYCASE (NPY_CDOUBLE, Complex) - ARRAYCASE (NPY_BOOL, bool) - ARRAYCASE (NPY_CHAR, char) - ARRAYCASE (NPY_STRING, char) - - default: - throw object_convert_exception ( - PyEval_GetFuncName ((PyObject*)pyarr) - + (PyEval_GetFuncDesc ((PyObject*)pyarr) - + std::string (": Unsupported Python array type"))); - } - } - - template - static void - pyarrobj_to_octvalueNd (octave_value& octvalue, PyArrayObject *pyarr, - const dim_vector& dims) - { - OctaveBase array (dims); - copy_pyarrobj_to_octarray_boot (array, pyarr); - octvalue = array; - } - - static void - pyarr_to_octvalue (octave_value& octvalue, PyArrayObject *pyarr) - { - dim_vector dims; - switch (PyArray_NDIM (pyarr)) - { - case 0: - dims = dim_vector (1, 1); - break; - case 1: - // Always make PyArray vectors row vectors. - dims = dim_vector (1, PyArray_DIM (pyarr, 0)); - break; - default: - dims.resize (PyArray_NDIM (pyarr)); - for (int d = 0; d < PyArray_NDIM (pyarr); d++) - dims(d) = PyArray_DIM (pyarr, d); - break; - } - - switch (PyArray_TYPE (pyarr)) - { - case NPY_BYTE: - case NPY_SHORT: - case NPY_INT: - case NPY_LONG: - case NPY_LONGLONG: - switch (PyArray_ITEMSIZE (pyarr)) - { - case 1: - pyarrobj_to_octvalueNd (octvalue, pyarr, dims); - break; - case 2: - pyarrobj_to_octvalueNd (octvalue, pyarr, dims); - break; - case 4: - pyarrobj_to_octvalueNd (octvalue, pyarr, dims); - break; - case 8: - pyarrobj_to_octvalueNd (octvalue, pyarr, dims); - break; - default: - throw object_convert_exception ("Unknown integer."); - } - break; - case NPY_UBYTE: - case NPY_USHORT: - case NPY_UINT: - case NPY_ULONG: - case NPY_ULONGLONG: - switch (PyArray_ITEMSIZE (pyarr)) - { - case 1: - pyarrobj_to_octvalueNd (octvalue, pyarr, dims); - break; - case 2: - pyarrobj_to_octvalueNd (octvalue, pyarr, dims); - break; - case 4: - pyarrobj_to_octvalueNd (octvalue, pyarr, dims); - break; - case 8: - pyarrobj_to_octvalueNd (octvalue, pyarr, dims); - break; - default: - throw object_convert_exception ("Unknown unsigned integer."); - } - break; - case NPY_FLOAT: - pyarrobj_to_octvalueNd (octvalue, pyarr, dims); - break; - case NPY_DOUBLE: - pyarrobj_to_octvalueNd (octvalue, pyarr, dims); - break; - case NPY_CFLOAT: - pyarrobj_to_octvalueNd (octvalue, pyarr, dims); - break; - case NPY_CDOUBLE: - pyarrobj_to_octvalueNd (octvalue, pyarr, dims); - break; - case NPY_BOOL: - pyarrobj_to_octvalueNd (octvalue, pyarr, dims); - break; - case NPY_CHAR: - case_NPY_CHAR: - pyarrobj_to_octvalueNd (octvalue, pyarr, dims); - // FIXME: is the following needed? - octvalue = octvalue.convert_to_str (true, true, '"'); - break; - case NPY_STRING: - { - if (PyArray_ITEMSIZE (pyarr) == 1) - goto case_NPY_CHAR; - else - { - // Create a new descriptor of the data. - PyArray_Descr *view_descr = PyArray_DescrFromType (NPY_CHAR); - // Create a new view of the NumPy array. - PyArrayObject *view = (PyArrayObject *)PyArray_View (pyarr, view_descr, 0); - // Store in a handle to ensure proper destruction. - boost::python::handle<> view_handle (boost::python::allow_null ((PyObject *)view)); - // Call recursively. - pyarr_to_octvalue (octvalue, view); - } - } - break; - default: - throw object_convert_exception ( - PyEval_GetFuncDesc ((PyObject*)(pyarr)) + std::string (" ") - + PyEval_GetFuncName ((PyObject*)(pyarr)) - + ": Encountered unsupported Python array"); - break; - } - } - - void pyobj_to_octvalue (octave_value& oct_value, - const boost::python::object& py_object) - { - - if (PyBool_Check (py_object.ptr ())) - oct_value = extract_py_bool (py_object.ptr ()); -#if PY_VERSION_HEX < 0x03000000 - else if (PyInt_Check (py_object.ptr ())) - oct_value = octave_int64 (extract_py_int64 (py_object.ptr ())); -#endif - else if (PyFloat_Check (py_object.ptr ())) - oct_value = extract_py_float (py_object.ptr ()); - else if (PyComplex_Check (py_object.ptr ())) - oct_value = extract_py_complex (py_object.ptr ()); - else if (py_isinstance (py_object.ptr (), "numpy.ndarray")) - pyarr_to_octvalue (oct_value, (PyArrayObject*)py_object.ptr ()); - else - oct_value = pyobject_wrap_object (py_object.ptr ()); - } -} diff -r 95c6ad0be828 -r 3613ffbd52b2 python_to_octave.h --- a/python_to_octave.h Tue May 02 17:40:01 2017 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* - -Copyright (C) 2015-2016 Mike Miller -Copyright (C) 2008 David Grundberg, Håkan Fors Nilsson - -This file is part of Pytave. - -Pytave is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation, either version 3 of the License, or (at your -option) any later version. - -Pytave is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received a copy of the GNU General Public License -along with Pytave; see the file COPYING. If not, see -. - -*/ - -/*! \file python_to_octave.h - \brief Conversion of data values from Python to GNU Octave - - The functions in this file define the translation of Python - objects to GNU Octave values using the Boost.Python library and - the Octave/C++ API. - - Features and capabilities of Octave's Python interface include: - - - Import and call Python modules and functions from the Octave interpreter - - - Automatically convert basic Octave and Python types seamlessly between the two environments - - - \todo Be able to handle arbitrary unknown Python objects (print their repr, store in a variable, pass back in to a Python function) - - - \todo Store references to Python functions (and other "callables") and be able to call them as if they were function handles - -*/ - -#if ! defined (pytave_python_to_octave_h) -#define pytave_python_to_octave_h - -#include -#include - -namespace pytave -{ - - //! Conversion from any Python object to an Octave value object. - /*! - \param oct_value a reference to octave_value to store the result of the conversion. - \param py_object a constant reference to a boost::python::object that contains the object to be converted. - \see octvalue_to_pyobj - */ - void pyobj_to_octvalue (octave_value& oct_value, - const boost::python::object& py_object); -} - -#endif