changeset 351:040aff46e4db

Refactor calling a Python function into a set of wrapper functions * oct-py-eval.cc, oct-py-eval.h (pytave::py_call_function): New function overloads to call a Python function in various ways. * pycall.cc (Fpycall): Use pytave::py_call_function.
author Mike Miller <mtmiller@octave.org>
date Wed, 17 Aug 2016 14:02:33 -0700
parents e89a8a37fd8a
children eac35d84ef0d
files oct-py-eval.cc oct-py-eval.h pycall.cc
diffstat 3 files changed, 116 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/oct-py-eval.cc	Thu Aug 11 10:29:18 2016 -0700
+++ b/oct-py-eval.cc	Wed Aug 17 14:02:33 2016 -0700
@@ -26,13 +26,78 @@
 
 #include <string>
 #include <boost/python.hpp>
+#include <octave/ov.h>
+#include <octave/ovl.h>
 
 #include "oct-py-eval.h"
+#include "oct-py-util.h"
+#include "octave_to_python.h"
 
 namespace pytave
 {
 
 PyObject *
+py_call_function (const std::string& func, const octave_value_list& args)
+{
+  // FIXME: factor out parsing of string into a function reference
+  boost::python::object obj;
+  get_object_from_python (func, obj);
+  return py_call_function (obj.ptr (), args);
+}
+
+PyObject *
+py_call_function (const std::string& func, PyObject *args, PyObject *kwargs)
+{
+  // FIXME: factor out parsing of string into a function reference
+  boost::python::object obj;
+  get_object_from_python (func, obj);
+  return py_call_function (obj.ptr (), args, kwargs);
+}
+
+PyObject *
+py_call_function (PyObject *callable, const octave_value_list& args)
+{
+  PyObject *kwargs = 0;
+  PyObject *args_list = PyList_New (0);
+  if (! args_list)
+    octave_throw_bad_alloc ();
+
+  for (int i = 0; i < args.length (); ++i)
+    {
+      boost::python::object arg;
+      pytave::octvalue_to_pyobj (arg, args(i));
+      PyObject *obj = arg.ptr ();
+
+      if (pytave::is_py_kwargs_argument (obj))
+        kwargs = pytave::update_py_dict (kwargs, obj);
+      else
+        {
+          Py_INCREF (obj);
+          PyList_Append (args_list, obj);
+        }
+    }
+
+  PyObject *args_tuple = PyList_AsTuple (args_list);
+  Py_DECREF (args_list);
+
+  PyObject *retval =  py_call_function (callable, args_tuple, kwargs);
+  Py_DECREF (args_tuple);
+  Py_XDECREF (kwargs);
+
+  return retval;
+}
+
+PyObject *
+py_call_function (PyObject *callable, PyObject *args, PyObject *kwargs)
+{
+  PyObject *retval = PyEval_CallObjectWithKeywords (callable, args, kwargs);
+  if (! retval)
+    throw boost::python::error_already_set ();
+
+  return retval;
+}
+
+PyObject *
 py_run_string_safe (const std::string& expr, int start, PyObject *globals,
                     PyObject *locals)
 {
--- a/oct-py-eval.h	Thu Aug 11 10:29:18 2016 -0700
+++ b/oct-py-eval.h	Wed Aug 17 14:02:33 2016 -0700
@@ -26,9 +26,57 @@
 #include <Python.h>
 #include <string>
 
+class octave_value_list;
+
 namespace pytave
 {
 
+//! Call a Python function by name with the given argument list.
+//!
+//! The @a func string may be the name of a builtin function or of a function
+//! in a loadable module in the form @c module.function.
+//!
+//! If @a args contains one or more values created by the @c pyargs function,
+//! they are automatically interpreted as keyword arguments.
+//!
+//! @param func name of a Python function
+//! @param args Octave argument list to be converted and passed to @a func
+//! @return return value of @a func
+PyObject *
+py_call_function (const std::string& func, const octave_value_list& args);
+
+//! Call a Python function by name with arguments and keyword arguments.
+//!
+//! The @a func string may be the name of a builtin function or of a function
+//! in a loadable module in the form @c module.function.
+//!
+//! @param func name of a Python function
+//! @param args tuple of positional arguments
+//! @param kwargs dictionary of keyword arguments
+//! @return return value of @a func
+PyObject *
+py_call_function (const std::string& func, PyObject *args, PyObject *kwargs = 0);
+
+//! Call a Python function with the given argument list.
+//!
+//! If @a args contains one or more values created by the @c pyargs function,
+//! they are automatically interpreted as keyword arguments.
+//!
+//! @param callable Python function or other callable object
+//! @param args Octave argument list to be converted and passed to @a func
+//! @return return value of @a func
+PyObject *
+py_call_function (PyObject *callable, const octave_value_list& args);
+
+//! Call a Python function with arguments and keyword arguments.
+//!
+//! @param callable Python function or other callable object
+//! @param args tuple of positional arguments
+//! @param kwargs dictionary of keyword arguments
+//! @return return value of @a func
+PyObject *
+py_call_function (PyObject *callable, PyObject *args, PyObject *kwargs = 0);
+
 PyObject *
 py_eval_string (const std::string& expr, PyObject *globals = 0, PyObject *locals = 0);
 
--- a/pycall.cc	Thu Aug 11 10:29:18 2016 -0700
+++ b/pycall.cc	Wed Aug 17 14:02:33 2016 -0700
@@ -33,6 +33,7 @@
 #define PYTAVE_DO_DECLARE_SYMBOL
 #include "arrayobjectdefs.h"
 #include "exceptions.h"
+#include "oct-py-eval.h"
 #include "oct-py-util.h"
 #include "octave_to_python.h"
 #include "python_to_octave.h"
@@ -101,32 +102,10 @@
       if (callable.is_none ())
         error("pycall: FUNC must be a string or a Python reference");
 
-      PyObject *args_list = PyList_New (0);
-      PyObject *kwargs = 0;
-      for (int i = 1; i < nargin; i++)
-        {
-          object arg;
-          pytave::octvalue_to_pyobj (arg, args(i));
-          PyObject *obj = arg.ptr ();
-
-          if (pytave::is_py_kwargs_argument (obj))
-            kwargs = pytave::update_py_dict (kwargs, obj);
-          else
-            {
-              Py_INCREF (obj);
-              PyList_Append (args_list, obj);
-            }
-        }
-
-      PyObject *pyargs = PyList_AsTuple (args_list);
-      PyObject *result = PyEval_CallObjectWithKeywords (callable.ptr (), pyargs, kwargs);
+      octave_value_list arglist = args.slice (1, nargin - 1);
+      PyObject *result = pytave::py_call_function (callable.ptr (), arglist);
       object res = object (handle<PyObject> (result));
 
-      Py_DECREF (args_list);
-      Py_DECREF (pyargs);
-      if (kwargs)
-        Py_DECREF (kwargs);
-
       // Ensure reasonable "ans" behaviour, consistent with Python's "_".
       if (nargout > 0 || ! res.is_none ())
         {