changeset 422:8247f298fd16

Raise Octave errors instead of exceptions for conversion errors * Makefile.am (COMMON_SOURCE_FILES): Include oct-py-error.cc in the list. (PYTAVE_HEADER_FILES): Include oct-py-error.h in the list. * oct-py-error.cc, oct-py-error.h: New files defining error conditions. * oct-py-types.cc (pytave::extract_py_bool, pytave::extract_py_complex, pytave::extract_py_float, pytave::make_py_array, pytave::extract_py_scalar_map, pytave::extract_py_int64, pytave::extract_py_uint64, pytave::extract_py_str): Use new error condition functions instead of throwing exceptions. (pytave::make_py_array, pytave::make_py_numeric_value, pytave::make_py_tuple): Raise Octave error instead of throwing exceptions. * __py_struct_from_dict__.cc (F__py_int64_scalar_value__, F__py_uint64_scalar_value__): Remove exception handling logic. * pycall.cc (Fpycall): Remove conversion exception handlers. * pyeval.cc (Fpyeval): Likewise. * pyexec.cc (Fpyexec): Likewise. * exceptions.h (pytave::pytave_exception, pytave::octave_error_exception, pytave::octave_parse_exception, pytave::value_convert_exception, pytave::object_convert_exception, pytave::variable_name_exception): Delete no longer used exception types.
author Mike Miller <mtmiller@octave.org>
date Thu, 04 May 2017 17:13:38 -0700
parents 24555fba9964
children 6b9de18b4bdd
files Makefile.am __py_struct_from_dict__.cc exceptions.h oct-py-error.cc oct-py-error.h oct-py-types.cc pycall.cc pyeval.cc pyexec.cc
diffstat 9 files changed, 183 insertions(+), 180 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.am	Thu May 04 13:22:20 2017 -0700
+++ b/Makefile.am	Thu May 04 17:13:38 2017 -0700
@@ -30,6 +30,7 @@
 
 COMMON_SOURCE_FILES = \
   exceptions.cc \
+  oct-py-error.cc \
   oct-py-eval.cc \
   oct-py-init.cc \
   oct-py-types.cc \
@@ -66,6 +67,7 @@
 PYTAVE_HEADER_FILES = \
   config.h \
   exceptions.h \
+  oct-py-error.h \
   oct-py-eval.h \
   oct-py-init.h \
   oct-py-object.h \
--- a/__py_struct_from_dict__.cc	Thu May 04 13:22:20 2017 -0700
+++ b/__py_struct_from_dict__.cc	Thu May 04 17:13:38 2017 -0700
@@ -27,7 +27,6 @@
 #include <Python.h>
 #include <octave/oct.h>
 
-#include "exceptions.h"
 #include "oct-py-init.h"
 #include "oct-py-object.h"
 #include "oct-py-types.h"
@@ -88,24 +87,7 @@
   pytave::py_init ();
 
   pytave::python_object obj = pytave::pyobject_unwrap_object (args(0));
-  if (! obj)
-    error ("pyobject.int64: argument must be a valid Python object");
-
-  octave_int64 retval;
-
-  try
-    {
-      retval = pytave::extract_py_int64 (obj);
-    }
-  catch (pytave::object_convert_exception const &)
-    {
-      error ("pyobject.int64: argument must be a Python int or long object");
-    }
-  catch (pytave::error_already_set const &)
-    {
-      std::string message = pytave::fetch_exception_message ();
-      error ("pyobject.int64: %s", message.c_str ());
-    }
+  octave_int64 retval = pytave::extract_py_int64 (obj);
 
   return ovl (retval);
 }
@@ -140,24 +122,7 @@
   pytave::py_init ();
 
   pytave::python_object obj = pytave::pyobject_unwrap_object (args(0));
-  if (! obj)
-    error ("pyobject.uint64: argument must be a valid Python object");
-
-  octave_uint64 retval;
-
-  try
-    {
-      retval = pytave::extract_py_uint64 (obj);
-    }
-  catch (pytave::object_convert_exception const &)
-    {
-      error ("pyobject.uint64: argument must be a Python int or long object");
-    }
-  catch (pytave::error_already_set const &)
-    {
-      std::string message = pytave::fetch_exception_message ();
-      error ("pyobject.uint64: %s", message.c_str ());
-    }
+  octave_uint64 retval = pytave::extract_py_uint64 (obj);
 
   return ovl (retval);
 }
--- a/exceptions.h	Thu May 04 13:22:20 2017 -0700
+++ b/exceptions.h	Thu May 04 17:13:38 2017 -0700
@@ -29,60 +29,6 @@
 
 namespace pytave
 {
-  class pytave_exception
-  {
-  public:
-    pytave_exception (const std::string& err) { error = err; };
-
-  private:
-    std::string error;
-  };
-
-  class octave_error_exception
-  {
-  public:
-    octave_error_exception (const std::string& err) { error = err; };
-
-  private:
-    std::string error;
-  };
-
-  class octave_parse_exception
-  {
-  public:
-    octave_parse_exception (const std::string& err) { error = err; };
-
-  private:
-    std::string error;
-  };
-
-  class value_convert_exception
-  {
-  public:
-    value_convert_exception (const std::string& err) { error = err; };
-
-  private:
-    std::string error;
-  };
-
-  class object_convert_exception
-  {
-  public:
-    object_convert_exception (const std::string& err) { error = err; };
-
-  private:
-    std::string error;
-  };
-
-  class variable_name_exception
-  {
-  public:
-    variable_name_exception (const std::string& err) { error = err; };
-
-  private:
-    std::string error;
-  };
-
   class error_already_set
   {
   };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/oct-py-error.cc	Thu May 04 17:13:38 2017 -0700
@@ -0,0 +1,48 @@
+/*
+
+Copyright (C) 2017 Mike Miller
+
+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 <octave/error.h>
+
+#include "oct-py-error.h"
+
+namespace pytave
+{
+
+  void
+  error_conversion_invalid_python_object (const std::string& to)
+  {
+    error ("unable to convert to %s, invalid Python object", to.c_str ());
+  }
+
+  void
+  error_conversion_mismatch_python_type (const std::string& to,
+                                         const std::string& must)
+  {
+    error ("unable to convert to %s, must be a Python %s", to.c_str (),
+           must.c_str ());
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/oct-py-error.h	Thu May 04 17:13:38 2017 -0700
@@ -0,0 +1,50 @@
+/*
+
+Copyright (C) 2017 Mike Miller
+
+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 (pytave_oct_py_error_h)
+#define pytave_oct_py_error_h 1
+
+#include <string>
+
+#if defined (__GNUC__)
+#  define PYTAVE_ATTR_NORETURN __attribute__((__noreturn__))
+#else
+#  define PYTAVE_ATTR_NORETURN
+#endif
+
+namespace pytave
+{
+
+  void
+  error_conversion_invalid_python_object (const std::string& to)
+  PYTAVE_ATTR_NORETURN;
+
+  void
+  error_conversion_mismatch_python_type (const std::string& to,
+                                         const std::string& must)
+  PYTAVE_ATTR_NORETURN;
+
+}
+
+#undef PYTAVE_ATTR_NORETURN
+
+#endif
--- a/oct-py-types.cc	Thu May 04 13:22:20 2017 -0700
+++ b/oct-py-types.cc	Thu May 04 17:13:38 2017 -0700
@@ -32,6 +32,7 @@
 #include <octave/ov-null-mat.h>
 
 #include "exceptions.h"
+#include "oct-py-error.h"
 #include "oct-py-eval.h"
 #include "oct-py-object.h"
 #include "oct-py-types.h"
@@ -65,10 +66,10 @@
   extract_py_bool (PyObject *obj)
   {
     if (! obj)
-      throw object_convert_exception ("failed to extract boolean: null object");
+      error_conversion_invalid_python_object ("a boolean value");
 
     if (! PyBool_Check (obj))
-      throw object_convert_exception ("failed to extract boolean: wrong type");
+      error_conversion_mismatch_python_type ("a boolean value", "bool");
 
     return (obj == Py_True);
   }
@@ -77,10 +78,10 @@
   extract_py_complex (PyObject *obj)
   {
     if (! obj)
-      throw object_convert_exception ("failed to extract complex: null object");
+      error_conversion_invalid_python_object ("a complex value");
 
     if (! PyComplex_Check (obj))
-      throw object_convert_exception ("failed to extract complex: wrong type");
+      error_conversion_mismatch_python_type ("a complex value", "complex");
 
     Py_complex value = PyComplex_AsCComplex (obj);
     return std::complex<double> {value.real, value.imag};
@@ -90,10 +91,10 @@
   extract_py_float (PyObject *obj)
   {
     if (! obj)
-      throw object_convert_exception ("failed to extract float: null object");
+      error_conversion_invalid_python_object ("a floating point value");
 
     if (! PyFloat_Check (obj))
-      throw object_convert_exception ("failed to extract float: wrong type");
+      error_conversion_mismatch_python_type ("a floating point value", "float");
 
     return PyFloat_AsDouble (obj);
   }
@@ -138,8 +139,7 @@
   make_py_array (const void *data, size_t len, char typecode)
   {
     if (! typecode)
-      throw object_convert_exception
-        ("unable to create array from Octave data");
+      error ("unable to create array, invalid array type code");
 
     std::string arg { typecode };
     python_object array = py_call_function ("array.array", ovl (arg));
@@ -242,81 +242,88 @@
   PyObject *
   make_py_numeric_value (const octave_value& value)
   {
-    if (value.is_scalar_type ())
-      {
-        if (value.is_bool_type ())
-          return make_py_bool (value.bool_value ());
+    if (! value.is_scalar_type ())
+      error ("unable to convert non-scalar type \"%s\" to a Python number",
+             value.type_name ().c_str ());
+
+    if (value.is_bool_type ())
+      return make_py_bool (value.bool_value ());
 
-        else if (value.is_int8_type ())
-          return make_py_int (value.int8_scalar_value ().value ());
-        else if (value.is_int16_type ())
-          return make_py_int (value.int16_scalar_value ().value ());
-        else if (value.is_int32_type ())
-          return make_py_int (value.int32_scalar_value ().value ());
-        else if (value.is_int64_type ())
-          return make_py_int (value.int64_scalar_value ().value ());
+    else if (value.is_int8_type ())
+      return make_py_int (value.int8_scalar_value ().value ());
+    else if (value.is_int16_type ())
+      return make_py_int (value.int16_scalar_value ().value ());
+    else if (value.is_int32_type ())
+      return make_py_int (value.int32_scalar_value ().value ());
+    else if (value.is_int64_type ())
+      return make_py_int (value.int64_scalar_value ().value ());
 
-        else if (value.is_uint8_type ())
-          return make_py_int (value.uint8_scalar_value ().value ());
-        else if (value.is_uint16_type ())
-          return make_py_int (value.uint16_scalar_value ().value ());
-        else if (value.is_uint32_type ())
-          return make_py_int (value.uint32_scalar_value ().value ());
-        else if (value.is_uint64_type ())
-          return make_py_int (value.uint64_scalar_value ().value ());
+    else if (value.is_uint8_type ())
+      return make_py_int (value.uint8_scalar_value ().value ());
+    else if (value.is_uint16_type ())
+      return make_py_int (value.uint16_scalar_value ().value ());
+    else if (value.is_uint32_type ())
+      return make_py_int (value.uint32_scalar_value ().value ());
+    else if (value.is_uint64_type ())
+      return make_py_int (value.uint64_scalar_value ().value ());
 
-        else if (value.is_complex_type ())
-          return make_py_complex (value.complex_value ());
-        else if (value.is_float_type ())
-          return make_py_float (value.double_value ());
-      }
+    else if (value.is_complex_type ())
+      return make_py_complex (value.complex_value ());
+    else if (value.is_float_type ())
+      return make_py_float (value.double_value ());
+    else
+      error ("unable to convert unhandled scalar type \"%s\" to a "
+             "Python number", value.type_name ().c_str ());
 
-    throw value_convert_exception ("unhandled scalar type");
-    return 0;
+    return nullptr;
   }
 
   PyObject *
   make_py_array (const octave_value& value)
   {
-    if (value.is_numeric_type () && ! value.is_complex_type ()
-        && value.ndims () == 2 && (value.columns () <= 1 || value.rows () <= 1))
-      {
-        if (value.is_double_type ())
-          return make_py_array (value.array_value ());
-        else if (value.is_single_type ())
-          return make_py_array (value.float_array_value ());
+    if (! (value.is_numeric_type () && ! value.is_complex_type ()
+           && value.ndims () == 2
+           && (value.columns () <= 1 || value.rows () <= 1)))
+      error ("unable to convert non-vector type \"%s\" to a Python array",
+             value.type_name ().c_str ());
+
+    if (value.is_double_type ())
+      return make_py_array (value.array_value ());
+    else if (value.is_single_type ())
+      return make_py_array (value.float_array_value ());
 
-        else if (value.is_int8_type ())
-          return make_py_array (value.int8_array_value ());
-        else if (value.is_int16_type ())
-          return make_py_array (value.int16_array_value ());
-        else if (value.is_int32_type ())
-          return make_py_array (value.int32_array_value ());
-        else if (value.is_int64_type ())
-          return make_py_array (value.int64_array_value ());
+    else if (value.is_int8_type ())
+      return make_py_array (value.int8_array_value ());
+    else if (value.is_int16_type ())
+      return make_py_array (value.int16_array_value ());
+    else if (value.is_int32_type ())
+      return make_py_array (value.int32_array_value ());
+    else if (value.is_int64_type ())
+      return make_py_array (value.int64_array_value ());
 
-        else if (value.is_uint8_type ())
-          return make_py_array (value.uint8_array_value ());
-        else if (value.is_uint16_type ())
-          return make_py_array (value.uint16_array_value ());
-        else if (value.is_uint32_type ())
-          return make_py_array (value.uint32_array_value ());
-        else if (value.is_uint64_type ())
-          return make_py_array (value.uint64_array_value ());
-      }
+    else if (value.is_uint8_type ())
+      return make_py_array (value.uint8_array_value ());
+    else if (value.is_uint16_type ())
+      return make_py_array (value.uint16_array_value ());
+    else if (value.is_uint32_type ())
+      return make_py_array (value.uint32_array_value ());
+    else if (value.is_uint64_type ())
+      return make_py_array (value.uint64_array_value ());
+    else
+      error ("unable to convert unhandled vector type \"%s\" to a "
+             "Python array", value.type_name ().c_str ());
 
-    throw value_convert_exception ("unhandled Octave numeric vector type");
-    return 0;
+    return nullptr;
   }
 
   octave_scalar_map
   extract_py_scalar_map (PyObject *obj)
   {
     if (! obj)
-      error ("unable to convert to an Octave struct, invalid Python object");
+      error_conversion_invalid_python_object ("an Octave struct");
 
     if (! PyDict_Check (obj))
-      error ("unable to convert to an Octave struct, must be a Python dict");
+      error_conversion_mismatch_python_type ("an Octave struct", "dict");
 
     octave_scalar_map map;
 
@@ -364,7 +371,7 @@
   extract_py_int64 (PyObject *obj)
   {
     if (! obj)
-      throw object_convert_exception ("failed to extract integer: null object");
+      error_conversion_invalid_python_object ("a signed integer value");
 
     if (PyLong_Check (obj))
       {
@@ -388,7 +395,8 @@
       return PyInt_AsLong (obj);
 #endif
     else
-      throw object_convert_exception ("failed to extract integer: wrong type");
+      error_conversion_mismatch_python_type ("a signed integer value",
+                                             "int or long");
 
     return 0;
   }
@@ -397,7 +405,7 @@
   extract_py_uint64 (PyObject *obj)
   {
     if (! obj)
-      throw object_convert_exception ("failed to extract integer: null object");
+      error_conversion_invalid_python_object ("an unsigned integer value");
 
     if (PyLong_Check (obj))
       {
@@ -425,7 +433,8 @@
       return static_cast<uint64_t> (PyInt_AsLong (obj));
 #endif
     else
-      throw object_convert_exception ("failed to extract integer: wrong type");
+      error_conversion_mismatch_python_type ("an unsigned integer value",
+                                             "int or long");
 
     return 0;
   }
@@ -434,8 +443,7 @@
   make_py_tuple (const Cell& cell)
   {
     if (! (cell.is_empty () || cell.is_vector ()))
-      throw value_convert_exception (
-        "unable to convert multidimensional cell array into Python tuple");
+      error ("unable to convert multidimensional cell array to a Python tuple");
 
     octave_idx_type size = cell.numel ();
     PyObject *tuple = PyTuple_New (size);
@@ -457,7 +465,8 @@
     std::string retval;
 
     if (! obj)
-      throw object_convert_exception ("failed to extract string: null object");
+      error_conversion_invalid_python_object ("a string value");
+
     if (PyBytes_Check (obj))
       {
         retval.assign (PyBytes_AsString (obj), PyBytes_Size (obj));
@@ -468,11 +477,10 @@
         if (enc && PyBytes_Check (enc))
           retval.assign (PyBytes_AsString (enc), PyBytes_Size (enc));
         else
-          throw object_convert_exception
-            ("failed to extract string: UTF-8 error");
+          octave_throw_bad_alloc ();
       }
     else
-      throw object_convert_exception ("failed to extract string: wrong type");
+      error_conversion_mismatch_python_type ("a string value", "str");
 
     return retval;
   }
--- a/pycall.cc	Thu May 04 13:22:20 2017 -0700
+++ b/pycall.cc	Thu May 04 17:13:38 2017 -0700
@@ -113,14 +113,6 @@
       if (nargout > 0 || ! res.is_none ())
         retval(0) = pytave::py_implicitly_convert_return_value (res);
     }
-  catch (pytave::object_convert_exception const &)
-    {
-      error ("pyexec: error in return value type conversion");
-    }
-  catch (pytave::value_convert_exception const &)
-    {
-      error ("pycall: error in argument type conversion");
-    }
   catch (pytave::error_already_set const &)
     {
       std::string message = pytave::fetch_exception_message ();
--- a/pyeval.cc	Thu May 04 13:22:20 2017 -0700
+++ b/pyeval.cc	Thu May 04 17:13:38 2017 -0700
@@ -88,10 +88,6 @@
       if (nargout > 0 || ! res.is_none ())
         retval(0) = pytave::py_implicitly_convert_return_value (res);
     }
-  catch (pytave::object_convert_exception const &)
-    {
-      error ("pyexec: error in return value type conversion");
-    }
   catch (pytave::error_already_set const &)
     {
       std::string message = pytave::fetch_exception_message ();
--- a/pyexec.cc	Thu May 04 13:22:20 2017 -0700
+++ b/pyexec.cc	Thu May 04 17:13:38 2017 -0700
@@ -79,10 +79,6 @@
       // FIXME: figure out exec return code:
       pytave::py_exec_string (code, 0, local_namespace);
     }
-  catch (pytave::object_convert_exception const &)
-    {
-      error ("pyexec: error in return value type conversion");
-    }
   catch (pytave::error_already_set const &)
     {
       std::string message = pytave::fetch_exception_message ();