changeset 333:96b78e78a235

pycall: add support for keyword arguments from pyargs (fixes issue #45) * oct-py-util.cc, oct-py-util.h (pytave::py_object_class_name, pytave::is_py_kwargs_argument, pytave::update_py_dict): New functions. * pycall.cc (Fpycall): Check for and handle keyword arguments.
author Mike Miller <mtmiller@octave.org>
date Mon, 15 Aug 2016 13:20:15 -0700
parents b8128a2e0e70
children 252b458bd904
files oct-py-util.cc oct-py-util.h pycall.cc
diffstat 3 files changed, 57 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/oct-py-util.cc	Mon Aug 15 12:43:51 2016 -0700
+++ b/oct-py-util.cc	Mon Aug 15 13:20:15 2016 -0700
@@ -27,6 +27,7 @@
 #include <oct.h>
 #include <octave/parse.h>
 
+#include "oct-py-types.h"
 #include "oct-py-util.h"
 
 using namespace boost::python;
@@ -95,4 +96,33 @@
     }
 }
 
+std::string
+py_object_class_name (PyObject *obj)
+{
+  PyObject *class_ = obj ? PyObject_GetAttrString (obj, "__class__") : 0;
+  PyObject *name_ = class_ ? PyObject_GetAttrString (class_, "__name__") : 0;
+  return name_ ? extract_py_str (name_): "";
 }
+
+bool
+is_py_kwargs_argument (PyObject *obj)
+{
+  if (obj && py_object_class_name (obj) == "_OctaveKwargs"
+      && PyObject_HasAttrString (obj, "is_kwargs_argument"))
+    {
+      PyObject *flag = PyObject_GetAttrString (obj, "is_kwargs_argument");
+      if (flag && PyBool_Check (flag) && PyObject_IsTrue (flag))
+        return true;
+    }
+  return false;
+}
+
+PyObject *
+update_py_dict (PyObject *dict_orig, PyObject *dict_new)
+{
+  PyObject *dict = dict_orig ? dict_orig : PyDict_New ();
+  PyDict_Update (dict, dict_new);
+  return dict;
+}
+
+}
--- a/oct-py-util.h	Mon Aug 15 12:43:51 2016 -0700
+++ b/oct-py-util.h	Mon Aug 15 13:20:15 2016 -0700
@@ -23,6 +23,7 @@
 #if ! defined (pytave_oct_py_util_h)
 #define pytave_oct_py_util_h
 
+#include <string>
 #include <boost/python.hpp>
 
 class octave_value;
@@ -37,6 +38,15 @@
 get_object_from_python (const octave_value& oct_value,
                         boost::python::object& py_object);
 
+std::string
+py_object_class_name (PyObject *obj);
+
+bool
+is_py_kwargs_argument (PyObject *obj);
+
+PyObject *
+update_py_dict (PyObject *dict_orig, PyObject *dict_new);
+
 }
 
 #endif
--- a/pycall.cc	Mon Aug 15 12:43:51 2016 -0700
+++ b/pycall.cc	Mon Aug 15 13:20:15 2016 -0700
@@ -101,19 +101,32 @@
       if (callable.is_none ())
         error("pycall: FUNC must be a string or a Python reference");
 
-      PyObject *pyargs = PyTuple_New (nargin - 1);
+      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 ();
-          Py_INCREF (obj);
-          PyTuple_SET_ITEM (pyargs, i - 1, obj);
+
+          if (pytave::is_py_kwargs_argument (obj))
+            kwargs = pytave::update_py_dict (kwargs, obj);
+          else
+            {
+              Py_INCREF (obj);
+              PyList_Append (args_list, obj);
+            }
         }
 
-      PyObject *result = PyEval_CallObjectWithKeywords (callable.ptr (), pyargs, 0);
+      PyObject *pyargs = PyList_AsTuple (args_list);
+      PyObject *result = PyEval_CallObjectWithKeywords (callable.ptr (), pyargs, kwargs);
       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 ())
         {