help-octave
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Octave as a module for Python


From: David Grundberg
Subject: Re: Octave as a module for Python
Date: Wed, 06 May 2009 14:34:51 +0200
User-agent: Thunderbird 2.0.0.21 (X11/20090318)

Jaroslav Hajek skrev:
On Tue, May 5, 2009 at 9:32 PM, David Grundberg <address@hidden> wrote:
Announcing Pytave

Hi David,

I have wrapped also the "eval" function into pytave - see the attached
patch (created using bzr diff). How should I go about it? Should
contributions be reported as bugs using the launchpad tracker? Sorry I
have no experience with neither bazaar nor launchpad.

summary:
"eval" is provided as a wrapper to eval_string (parse.h). In
principle, this could be achieved more simply using feval("eval", but
the advantages are:
1. faster (avoids double call and double conversion of code string)
2. explicit control of printing rather than implicitly with nargout (as in eval)
3. a separate exception class for parse error

regards

Hi Jaroslav,

Glad to see you improving my software! As your patch is of a manageable size, you might as well open a bug report and attach your patch.

I have some comments

1. As the code stands, it is not possible to catch pytave.ParseError. ([module pytave has] "no attribute 'ParseError'") In order to be correctly declared, the octave_parse_exception will have to be returned by the _pytave::get_exceptions function and correctly assigned to a symbol named ParseError in pytave.py. 2. It's unnecessary to duplicate the value conversion documentation, I think it'll be easier to maintain if it just referred to feval.

I'm attaching a modified patch as a suggestion. I modified the semantics of the nargout parameter since 0 is valid input.

Furthermore, I see that the builtin eval function accepts a second code string, CATCH. What are the purpose of this argument, and why did you not include it?

sincerely,
David
=== modified file 'exceptions.cc'
--- exceptions.cc       2008-10-18 10:28:00 +0000
+++ exceptions.cc       2009-05-06 11:42:53 +0000
@@ -25,6 +25,7 @@
        PyObject *octave_error_exception::excclass = NULL;
        PyObject *value_convert_exception::excclass = NULL;
        PyObject *object_convert_exception::excclass = NULL;
+       PyObject *octave_parse_exception::excclass = NULL;
 
 }
 

=== modified file 'exceptions.h'
--- exceptions.h        2009-05-03 18:24:48 +0000
+++ exceptions.h        2009-05-06 12:13:29 +0000
@@ -55,6 +55,25 @@
 
    };
 
+   class octave_parse_exception {
+      public:
+         static bool init() {
+            excclass = PyErr_NewException(
+               const_cast<char*>("pytave.ParseError"),
+               PyExc_RuntimeError, NULL);
+            return excclass != NULL;
+         };
+         static void translate_exception(octave_parse_exception const &py_ex) {
+            PyErr_SetString(excclass, py_ex.error.c_str());
+         }
+         static PyObject *excclass;
+
+         octave_parse_exception(std::string err) { error = err; };
+
+      private:
+         std::string error;
+   };
+
    class value_convert_exception {
       public:
          static bool init() {

=== modified file 'package/pytave.py'
--- package/pytave.py   2009-05-03 18:50:25 +0000
+++ package/pytave.py   2009-05-06 12:24:27 +0000
@@ -22,16 +22,16 @@
 import _pytave
 
 _pytave.init()
-(OctaveError, ValueConvertError, ObjectConvertError) \
+(OctaveError, ValueConvertError, ObjectConvertError, ParseError) \
                                  = _pytave.get_exceptions();
 
 def feval(nargout, funcname, *arguments):
 
        """Executes an Octave function called funcname.
 
-       The function is set to return nargout values. Returned values are
-       stored in a tuple. If the nargout argument is less than or equal
-       to 0, an empty tuple is returned.
+       The function is set to return nargout values. Returned values
+       are stored in a tuple. If the nargout argument is less than 0,
+       an empty tuple is returned.
 
        M-files are searched for in the Octave path.
 
@@ -96,6 +96,41 @@
 
        return _pytave.feval(nargout, funcname, arguments)
 
+def eval(nargout, code, silent=True):
+
+       """Executes a given Octave code.
+
+       The expression is expected to return nargout values. Returned
+       values are stored in a tuple. If the nargout argument is less
+       than 0, an empty tuple is returned.
+
+       All normal scope and function search rules apply. If silent is
+       true (default), the result is not auto-printed, as if a
+       semicolon was appended. Otherwise, auto-printing is enabled.
+
+       See also the Octave documentation for the builtin Octave
+       function eval.
+
+       For information about returned value conversion, see
+       pytave.feval.
+
+       Errors
+       ******
+
+       If the code cannot be parsed, a pytave.ParseError exception
+       occurs.
+
+       Octave runtime errors are encapsulated into pytave.OctaveError
+       exceptions, base class RuntimeError.
+
+       If the resulting values cannot be converted, a
+       pytave.ValueConvertError is raised. This exception inherits
+       TypeError.
+
+       """
+
+       return _pytave.eval(nargout, code, silent)
+
 def addpath(*arguments):
        """See Octave documentation"""
        return _pytave.feval(1, "addpath", arguments)[0]

=== modified file 'pytave.cc'
--- pytave.cc   2009-05-03 18:24:48 +0000
+++ pytave.cc   2009-05-06 12:23:34 +0000
@@ -48,7 +48,8 @@
 
       if (!octave_error_exception::init()
           || !value_convert_exception::init()
-          || !object_convert_exception::init()) {
+          || !object_convert_exception::init()
+          || !octave_parse_exception::init()) {
          PyErr_SetString(PyExc_ImportError, "_pytave: init failed");
          return;
       }
@@ -74,11 +75,45 @@
                         object(handle<PyObject>(
                                   value_convert_exception::excclass)),
                         object(handle<PyObject>(
-                                  object_convert_exception::excclass)));
-   }
-
+                                  object_convert_exception::excclass)),
+                        object(handle<PyObject>(
+                                  octave_parse_exception::excclass)));
+   }
+
+   string make_error_message (const Octave_map& map) {
+      ostringstream exceptionmsg;
+      string message = map.stringfield("message", "");
+      string identifier = map.stringfield("identifier", "");
+      Cell stackCell = map.contents("stack");
+
+      // Trim trailing new lines
+      message = message.substr(0, message.find_last_not_of("\r\n") + 1);
+
+      if (!stackCell.is_empty() && stackCell(0).is_map()) {
+         // The struct element is called "stack" but only contain
+         // info about the top frame.
+         Octave_map stack = stackCell(0).map_value();
+         string file = stack.stringfield("file", "");
+         string name = stack.stringfield("name", "");
+         int line = stack.intfield("line", 1);
+         int column = stack.intfield("column", 2);
+
+         exceptionmsg << file << ":" << line << ":" << column << ": ";
+         if (!name.empty())
+            exceptionmsg << "in '" << name << "': ";
+      }
+
+      if (!identifier.empty()) {
+         exceptionmsg << "(identifier: " << identifier << ") ";
+      }
+      exceptionmsg << message;
+
+      return exceptionmsg.str ();
+   }
+
+     
    boost::python::tuple func_eval(const int nargout,
-                                  const std::string &funcname,
+                                  const string &funcname,
                                   const boost::python::tuple &arguments) {
 
       octave_value_list octave_args, retval;
@@ -89,7 +124,7 @@
       buffer_error_messages++;
       
       Py_BEGIN_ALLOW_THREADS
-      retval = feval(funcname, octave_args, nargout);
+      retval = feval(funcname, octave_args, (nargout >= 0) ? nargout : 0);
       Py_END_ALLOW_THREADS
 
       if (error_state != 0) {
@@ -102,45 +137,63 @@
          octave_value_list lasterror = eval_string("lasterror",
                                                    true, parse_status, 1);
          if (!lasterror.empty() && lasterror(0).is_map()) {
-            ostringstream exceptionmsg;
-            Octave_map map = lasterror(0).map_value();
-            string message = map.stringfield("message", "");
-            string identifier = map.stringfield("identifier", "");
-            Cell stackCell = map.contents("stack");
-
-            // Trim trailing new lines
-            message = message.substr(0, message.find_last_not_of("\r\n") + 1);
-
-            if (!stackCell.is_empty() && stackCell(0).is_map()) {
-               // The struct element is called "stack" but only contain
-               // info about the top frame.
-               Octave_map stack = stackCell(0).map_value();
-               string file = stack.stringfield("file", "");
-               string name = stack.stringfield("name", "");
-               int line = stack.intfield("line", 1);
-               int column = stack.intfield("column", 2);
-
-               exceptionmsg << file << ":" << line << ":" << column << ": ";
-               if (!name.empty())
-                  exceptionmsg << "in '" << name << "': ";
-            }
-
-            if (!identifier.empty()) {
-               exceptionmsg << "(identifier: " << identifier << ") ";
-            }
-            exceptionmsg << message;
-
-            throw octave_error_exception(exceptionmsg.str());
-         } else
-            throw octave_error_exception("No Octave error available");
-      }
-
-      if (nargout > 0) {
-         boost::python::tuple pytuple;
-         octlist_to_pytuple(pytuple, retval);
-         return pytuple;
-      } else {
-         // Return () if nargout <= 0.
+            string exceptionmsg = make_error_message(lasterror(0).map_value 
());
+            throw octave_error_exception(exceptionmsg);
+         } else
+            throw octave_error_exception("No Octave error available");
+      }
+
+      if (nargout >= 0) {
+         boost::python::tuple pytuple;
+         octlist_to_pytuple(pytuple, retval);
+         return pytuple;
+      } else {
+         // Return () if nargout < 0.
+         return make_tuple();
+      }
+   }
+
+   boost::python::tuple str_eval(int nargout,
+                                 const string &code,
+                                 bool silent) {
+
+      octave_value_list retval;
+      int parse_status;
+
+      reset_error_handler();
+      buffer_error_messages++;
+      
+      Py_BEGIN_ALLOW_THREADS
+      retval = eval_string(code, silent, parse_status,
+         (nargout >= 0) ? nargout : 0);
+      Py_END_ALLOW_THREADS
+
+      if (parse_status != 0 || error_state != 0) {
+// error_state values:
+// -2 error without traceback
+// -1 traceback
+//  1 general error
+         int parse_status1 = 0;
+         reset_error_handler();
+         octave_value_list lasterror = eval_string("lasterror",
+                                                   true, parse_status1, 1);
+         if (!lasterror.empty() && lasterror(0).is_map()) {
+            string exceptionmsg = make_error_message (lasterror(0).map_value 
());
+
+            if (parse_status != 0)
+               throw octave_parse_exception(exceptionmsg);
+            else
+               throw octave_error_exception(exceptionmsg);
+         } else
+            throw octave_error_exception("No Octave error available");
+      }
+
+      if (nargout >= 0) {
+         boost::python::tuple pytuple;
+         octlist_to_pytuple(pytuple, retval);
+         return pytuple;
+      } else {
+         // Return () if nargout < 0.
          return make_tuple();
       }
    }
@@ -151,6 +204,7 @@
 
    def("init", pytave::init);
    def("feval", pytave::func_eval);
+   def("eval", pytave::str_eval);
    def("get_exceptions", pytave::get_exceptions);
 
    register_exception_translator<pytave::pytave_exception>(
@@ -159,6 +213,9 @@
    register_exception_translator<pytave::octave_error_exception>(
       pytave::octave_error_exception::translate_exception);
 
+   register_exception_translator<pytave::octave_parse_exception>(
+      pytave::octave_parse_exception::translate_exception);
+
    register_exception_translator<pytave::object_convert_exception>(
       pytave::object_convert_exception::translate_exception);
 

=== modified file 'test/test.py'
--- test/test.py        2009-05-05 18:57:24 +0000
+++ test/test.py        2009-05-06 12:26:38 +0000
@@ -86,12 +86,28 @@
        except Exception, e:
                print "FAIL", (value,), e
 
+def testparseerror(*value):
+       try:
+               print pytave.eval(*value);
+               print "FAIL:", (value,)
+       except pytave.ParseError:
+               pass
+       except Exception, e:
+               print "FAIL", (value,), e
+
 def testvalueok(*value):
        try:
                pytave.feval(1, *value);
        except Exception, e:
                print "FAIL", (value,), e
 
+def testevalexpect(numargout, code, expectations):
+       try:
+               results = pytave.eval(numargout, code);
+               if results != expectations:
+                       print "FAIL: eval: ", code, " because", results, " != 
", expectations, ","
+       except Exception, e:
+               print "FAIL: eval:", code, ":", e
 def testcellinvariant(value):
        pass
 
@@ -200,4 +216,7 @@
 if result.shape != (3, 1):
        print "FAIL: expected 3x1 matrix"
 
-
+testparseerror(1, "endfunction")
+testevalexpect(1, "2 + 2", (4,))
+testevalexpect(0, "{2}", ([2],))
+testevalexpect(2, "struct('foo', 2)", ({'foo': [2]},))


reply via email to

[Prev in Thread] Current Thread [Next in Thread]