changeset 411:3613ffbd52b2

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.
author Mike Miller <mtmiller@octave.org>
date Wed, 03 May 2017 16:30:45 -0700
parents 95c6ad0be828
children db29823a8919
files @pyobject/subsasgn.m @pyobject/subsref.m Makefile.am __py_struct_from_dict__.cc oct-py-eval.cc oct-py-types.cc oct-py-types.h octave_to_python.cc octave_to_python.h pycall.cc pyeval.cc pyexec.cc python_to_octave.cc python_to_octave.h
diffstat 14 files changed, 116 insertions(+), 734 deletions(-) [+]
line wrap: on
line diff
--- 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;
--- 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)
--- 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))
 
--- 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 <boost/python/errors.hpp>
 
 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));
 }
--- 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);
--- 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 <octave/Cell.h>
 #include <octave/oct-map.h>
 #include <octave/quit.h>
+#include <octave/ov-null-mat.h>
 
 #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 <boost/python/errors.hpp>
 
 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);
+  }
+
 }
--- 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
--- 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
-<http://www.gnu.org/licenses/>.
-
-*/
-
-#if defined (HAVE_CONFIG_H)
-#  include <config.h>
-#endif
-
-#include <boost/python.hpp>
-
-#include <octave/oct.h>
-#include <octave/ov.h>
-#include <octave/oct-map.h>
-#include <octave/parse.h>
-
-#include <iostream>
-#include "arrayobjectdefs.h"
-#include "exceptions.h"
-#include "octave_to_python.h"
-#include "oct-py-types.h"
-#include "oct-py-util.h"
-
-namespace pytave
-{
-
-  template <class PythonPrimitive, class OctaveBase>
-  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<PythonPrimitive, OctaveBase> (
-              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 <class PythonPrimitive, class OctaveBase>
-  static PyArrayObject *
-  create_array (const OctaveBase& octarr, int pyarraytype)
-  {
-    PyArrayObject *pyarr = createPyArr (octarr.dims (), pyarraytype);
-    try
-      {
-        copy_octarray_to_pyarrobj<PythonPrimitive, OctaveBase> (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<Complex, ComplexNDArray> (matrix.complex_array_value (), NPY_CDOUBLE);
-      else if (matrix.is_real_type ())
-        return create_array<double, NDArray> (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<FloatComplex, FloatComplexNDArray> (matrix.float_complex_array_value (), NPY_CFLOAT);
-      else if (matrix.is_real_type ())
-        return create_array<float, FloatNDArray> (matrix.float_array_value (), NPY_FLOAT);
-      else
-        throw value_convert_exception ("Unknown float matrix type");
-
-    if (matrix.is_int8_type ())
-      return create_array<int8_t, int8NDArray> (matrix.int8_array_value (), NPY_INT8);
-    if (matrix.is_int16_type ())
-      return create_array<int16_t, int16NDArray> (matrix.int16_array_value (), NPY_INT16);
-    if (matrix.is_int32_type ())
-      return create_array<int32_t, int32NDArray> (matrix.int32_array_value (), NPY_INT32);
-    if (matrix.is_int64_type ())
-      return create_array<int64_t, int64NDArray> (matrix.int64_array_value (), NPY_INT64);
-
-    if (matrix.is_uint8_type ())
-      return create_array<uint8_t, uint8NDArray> (matrix.uint8_array_value (), NPY_UINT8);
-    if (matrix.is_uint16_type ())
-      return create_array<uint16_t, uint16NDArray> (matrix.uint16_array_value (), NPY_UINT16);
-    if (matrix.is_uint32_type ())
-      return create_array<uint32_t, uint32NDArray> (matrix.uint32_array_value (), NPY_UINT32);
-    if (matrix.is_uint64_type ())
-      return create_array<uint64_t, uint64NDArray> (matrix.uint64_array_value (), NPY_UINT64);
-
-    if (matrix.is_bool_type ())
-      return create_array<bool, boolNDArray> (matrix.bool_array_value (), NPY_BOOL);
-    if (matrix.is_string ())
-      return create_array<char, charNDArray> (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");
-  }
-}
--- 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
-<http://www.gnu.org/licenses/>.
-
-*/
-
-/*! \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 <boost/python.hpp>
-#include <ov.h>
-
-//! 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
--- 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 <argument type conversion>
+%!error <unable to convert unhandled Octave type>
 %! pyexec ("def intwrapper(x): return int(x)");
 %! pycall ("intwrapper", ftp ());
 
--- 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 &)
     {
--- 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\
--- 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
-<http://www.gnu.org/licenses/>.
-
-*/
-
-#if defined (HAVE_CONFIG_H)
-#  include <config.h>
-#endif
-
-#include <iostream>
-#include <boost/python.hpp>
-#include <boost/type_traits/integral_constant.hpp>
-
-#include <octave/oct.h>
-#include <octave/oct-map.h>
-#include <octave/Cell.h>
-#include <octave/ov.h>
-#include <octave/Array.h>
-#include <octave/parse.h>
-
-#include "arrayobjectdefs.h"
-#include "exceptions.h"
-#include "oct-py-types.h"
-#include "oct-py-util.h"
-#include "python_to_octave.h"
-
-namespace pytave
-{
-  template <class PythonPrimitive, class OctaveBase>
-  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<PythonPrimitive, OctaveBase> (
-              matrix,
-              pyarr,
-              matindex + i*matstride,
-              matstride * PyArray_DIM (pyarr, dimension),
-              dimension + 1,
-              offset + i*PyArray_STRIDE (pyarr, dimension));
-          }
-      }
-  }
-
-  template <class PythonPrimitive, class OctaveBase>
-  static void
-  copy_pyarrobj_to_octarray_dispatch (OctaveBase& matrix,
-                                      PyArrayObject *pyarr,
-                                      const boost::true_type&)
-  {
-    copy_pyarrobj_to_octarray<PythonPrimitive, OctaveBase> (matrix, pyarr, 0, 1, 0, 0);
-  }
-
-  template <class PythonPrimitive, class OctaveBase>
-  static void
-  copy_pyarrobj_to_octarray_dispatch (OctaveBase& matrix,
-                                      PyArrayObject *pyarr,
-                                      const boost::false_type&)
-  {
-    assert (0);
-  }
-
-  template <class X, class Y> class matching_type : public boost::false_type { };
-  template <class X> class matching_type<X, X> : public boost::true_type { };
-  template <class X> class matching_type<X, octave_int<X> > : public boost::true_type { };
-  template <> class matching_type<float, double> : public boost::true_type { };
-  template <> class matching_type<FloatComplex, Complex> : public boost::true_type { };
-  template <> class matching_type<PyObject *, octave_value> : public boost::true_type { };
-
-  template <class PythonPrimitive, class OctaveBase>
-  static void
-  copy_pyarrobj_to_octarray_dispatch (OctaveBase& matrix,
-                                       PyArrayObject *pyarr)
-  {
-    matching_type<PythonPrimitive, typename OctaveBase::element_type> inst;
-    copy_pyarrobj_to_octarray_dispatch<PythonPrimitive, OctaveBase> (matrix, pyarr, inst);
-  }
-
-  template <class OctaveBase>
-  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<AC_primitive, OctaveBase> \
-         (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 <class OctaveBase>
-  static void
-  pyarrobj_to_octvalueNd (octave_value& octvalue, PyArrayObject *pyarr,
-                          const dim_vector& dims)
-  {
-    OctaveBase array (dims);
-    copy_pyarrobj_to_octarray_boot<OctaveBase> (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<int8NDArray> (octvalue, pyarr, dims);
-            break;
-          case 2:
-            pyarrobj_to_octvalueNd<int16NDArray> (octvalue, pyarr, dims);
-            break;
-          case 4:
-            pyarrobj_to_octvalueNd<int32NDArray> (octvalue, pyarr, dims);
-            break;
-          case 8:
-            pyarrobj_to_octvalueNd<int64NDArray> (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<uint8NDArray> (octvalue, pyarr, dims);
-            break;
-          case 2:
-            pyarrobj_to_octvalueNd<uint16NDArray> (octvalue, pyarr, dims);
-            break;
-          case 4:
-            pyarrobj_to_octvalueNd<uint32NDArray> (octvalue, pyarr, dims);
-            break;
-          case 8:
-            pyarrobj_to_octvalueNd<uint64NDArray> (octvalue, pyarr, dims);
-            break;
-          default:
-            throw object_convert_exception ("Unknown unsigned integer.");
-          }
-        break;
-      case NPY_FLOAT:
-        pyarrobj_to_octvalueNd<FloatNDArray> (octvalue, pyarr, dims);
-        break;
-      case NPY_DOUBLE:
-        pyarrobj_to_octvalueNd<NDArray> (octvalue, pyarr, dims);
-        break;
-      case NPY_CFLOAT:
-        pyarrobj_to_octvalueNd<FloatComplexNDArray> (octvalue, pyarr, dims);
-        break;
-      case NPY_CDOUBLE:
-        pyarrobj_to_octvalueNd<ComplexNDArray> (octvalue, pyarr, dims);
-        break;
-      case NPY_BOOL:
-        pyarrobj_to_octvalueNd<boolNDArray> (octvalue, pyarr, dims);
-        break;
-      case NPY_CHAR:
-      case_NPY_CHAR:
-        pyarrobj_to_octvalueNd<charNDArray> (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 ());
-  }
-}
--- 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
-<http://www.gnu.org/licenses/>.
-
-*/
-
-/*! \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 <boost/python.hpp>
-#include <ov.h>
-
-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