Mercurial > pytave
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 ()) {