Mercurial > pytave
diff pycall.cc @ 172:29d9da90afcf
pycall: New function to pass Octave values to a Python function
* pycall.cc: Rename from py.cc. Add support for dispatching variable
length Octave arguments to a Python function.
* Makefile.am (OCT_FILES): Rename py.oct to pycall.oct.
author | Mike Miller <mtmiller@octave.org> |
---|---|
date | Thu, 07 Apr 2016 14:51:18 -0700 |
parents | py.cc@eab5c6026303 |
children | 28dc607532c2 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pycall.cc Thu Apr 07 14:51:18 2016 -0700 @@ -0,0 +1,187 @@ +/* + +Copyright (C) 2015-2016 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 <boost/python.hpp> +#include <boost/python/numeric.hpp> + +#include <oct.h> + +#define PYTAVE_DO_DECLARE_SYMBOL +#include "arrayobjectdefs.h" +#include "exceptions.h" +#include "octave_to_python.h" +#include "python_to_octave.h" + +using namespace boost::python; + +DEFUN_DLD (pycall, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} pycall (@var{func})\n\ +@deftypefnx {Loadable Function} {@var{x} =} pycall (@var{func})\n\ +@deftypefnx {Loadable Function} {@var{x} =} pycall (@var{func}, @var{arg1}, @var{arg2}, @dots{})\n\ +Execute method of a Python module.\n\ +\n\ +Examples:\n\ +@example\n\ +@group\n\ +y = pycall('__builtin__.int(6)')\n\ + @result{} y = 6\n\ +pycall('sys.version')\n\ + @result{} ans = ...\n\ +pycall('__builtin__.eval(\"4+5\")')\n\ + @result{} ans = 9\n\ +pycall('__builtin__.dict(one=1,two=2)')\n\ + @result{} ans =\n\ + scalar structure containing the fields:\n\ + two = 2\n\ + one = 1\n\ +@end group\n\ +@end example\n\ +@end deftypefn") +{ + octave_value_list retval; + + int nargin = args.length (); + + if (nargin < 1) + { + print_usage (); + return retval; + } + + std::string module; + std::string func = args(0).string_value (); + + size_t idx = func.rfind ("."); + if (idx != std::string::npos) + { + module = func.substr (0, idx); + func = func.substr (idx + 1); + } + + Py_Initialize (); + + pytave::init_exceptions (); + numeric::array::set_module_and_type ("numpy", "ndarray"); + _import_array (); + + try + { + object main_module = import ("__main__"); + object main_namespace = main_module.attr ("__dict__"); + + object mod = (module.empty ()) ? main_module : import (module.c_str ()); + object callable = mod.attr (func.c_str ()); + + std::vector<object> pyargs; + for (int i = 1; i < nargin; i++) + { + object arg; + pytave::octvalue_to_pyobj (arg, args(i)); + pyargs.push_back (arg); + } + + object res; + + switch (nargin - 1) + { + case 0: + res = callable (); + break; + case 1: + res = callable (pyargs[0]); + break; + case 2: + res = callable (pyargs[0], pyargs[1]); + break; + case 3: + res = callable (pyargs[0], pyargs[1], pyargs[2]); + break; + case 4: + res = callable (pyargs[0], pyargs[1], pyargs[2], pyargs[3]); + break; + case 5: + res = callable (pyargs[0], pyargs[1], pyargs[2], pyargs[3], + pyargs[4]); + break; + case 6: + res = callable (pyargs[0], pyargs[1], pyargs[2], pyargs[3], + pyargs[4], pyargs[5]); + break; + case 7: + res = callable (pyargs[0], pyargs[1], pyargs[2], pyargs[3], + pyargs[4], pyargs[5], pyargs[6]); + break; + case 8: + res = callable (pyargs[0], pyargs[1], pyargs[2], pyargs[3], + pyargs[4], pyargs[5], pyargs[6], pyargs[7]); + break; + case 9: + res = callable (pyargs[0], pyargs[1], pyargs[2], pyargs[3], + pyargs[4], pyargs[5], pyargs[6], pyargs[7], + pyargs[8]); + break; + case 10: + res = callable (pyargs[0], pyargs[1], pyargs[2], pyargs[3], + pyargs[4], pyargs[5], pyargs[6], pyargs[7], + pyargs[8], pyargs[9]); + break; + default: + error ("pycall: more than 10 arguments are not yet supported"); + break; + } + + if (! res.is_none ()) + { + octave_value val; + pytave::pyobj_to_octvalue (val, res); + retval(0) = val; + } + } + catch (pytave::object_convert_exception const &) + { + error ("pycall: error in return value type conversion"); + } + catch (error_already_set const &) + { + PyObject *ptype, *pvalue, *ptraceback; + PyErr_Fetch (&ptype, &pvalue, &ptraceback); + + try + { + std::string message = extract<std::string> (pvalue); + error ("pycall: %s", message.c_str ()); + } + catch (error_already_set const &) + { + PyErr_Restore (ptype, pvalue, ptraceback); + PyErr_Print (); + } + } + + return retval; +} +