# HG changeset patch # User Mike Miller # Date 1471292415 25200 # Node ID 96b78e78a235a86f1a3898267d9f50d35471d0d3 # Parent b8128a2e0e7014777dfc4cec13711952b9ee0b73 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. diff -r b8128a2e0e70 -r 96b78e78a235 oct-py-util.cc --- 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 #include +#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; +} + +} diff -r b8128a2e0e70 -r 96b78e78a235 oct-py-util.h --- 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 #include 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 diff -r b8128a2e0e70 -r 96b78e78a235 pycall.cc --- 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 (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 ()) {