=== modified file 'exceptions.cc' --- exceptions.cc 2008-10-18 10:28:00 +0000 +++ exceptions.cc 2009-05-06 09:21:59 +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 09:28:18 +0000 @@ -55,6 +55,26 @@ }; + class octave_parse_exception { + public: + static bool init() { + excclass = PyErr_NewException( + const_cast("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 09:46:58 +0000 @@ -96,6 +96,62 @@ 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 + or equal to 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. + + Type conversions + **************** + + The following type conversions are supported: + + Octave to Python + ================ + + Scalar values to objects: + bool bool + real scalar float (64-bit) + any string* str + struct dict + cell* list + + * Cell arrays must be one-dimensional (row vector) and + character matrices must only have one row. Any + other form will raise a ValueConvertError. + + Matrix values to Numeric arrays: + int64 LONG + int32, uint32 INT, UINT + int16, uint16 SHORT, USHORT + int8, unint8 SBYTE, UBYTE + + All other values causes a pytave.ValueConvertError to be + raised. This exception inherits TypeError. + + Errors + ****** + + Octave runtime errors are encapsulated into + pytave.OctaveError exceptions, base class RuntimeError. + + If the code can't be parsed, a + pytave.ParseError exception occurs. + + """ + + 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 09:46:02 +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; } @@ -77,8 +78,40 @@ object_convert_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; @@ -102,35 +135,52 @@ 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()); + 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); + 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"); } @@ -151,6 +201,7 @@ def("init", pytave::init); def("feval", pytave::func_eval); + def("eval", pytave::str_eval); def("get_exceptions", pytave::get_exceptions); register_exception_translator( @@ -159,6 +210,9 @@ register_exception_translator( pytave::octave_error_exception::translate_exception); + register_exception_translator( + pytave::octave_parse_exception::translate_exception); + register_exception_translator( pytave::object_convert_exception::translate_exception);