1

I am trying to write a C-extension module for python3, say foo and I am trying to define method that can take keyword arguments.

static PyObject* fooImpl(PyObject*, PyObject*, PyObject*);
static PyObject* fooImpl2(PyObject, PyObject*);
static PyMethodDef fooMethods[] = {
    {"foo_impl", (PyCFunction) fooImpl, METH_VARARGS | METH_KEYWORDS, "Some description"},
    {"foo_impl2", fooImpl2, METH_VARARGS, "Some description"},
    {NULL, NULL, 0, NULL}
};

PyObject* fooImpl(PyObject* self, PyObject* args, PyObject* kwds) {
    static const char *keywordList[] = { "kw1", "kw2", NULL};
    PyObject *input = nullptr;
    PyObject *kw1Val = nullptr;
    PyObject *kw2Val = nullptr;
    PyObject *returnVal = nullptr;
    int err = PyArg_ParseTupleAndKeywords(args, kwds, "O|OO",
                                          const_cast<char**>(keywordList),
                                          &input, &kw1Val, &kw2Val);
    if (!err) {
       return NULL;
    }
    //// Do something with args to compute returnVal
    return returnVal;
}

When I try this within python, I get the following error

>>> import foo as fp
>>> arg1 = ...
>>> arg2 = ...
>>> arg3 = ...
>>> a = fp.foo_impl(arg1, kw1 = arg2, kw2 = arg3);
TypeError: function takes at most 2 arguments (3 given)

Seems like the interpreter is not registering the METH_KEYWORDS flag in the PyMethodDef . Is there someother way to add PyCFunctionWithKeywords methods to C-extension in Python3 . The only source I found was this stackoverflow post that goes back to the Python documentation here

Any help is deeply appreciated

Community
  • 1
  • 1

1 Answers1

2

You didn't define all the keywords. Even if the argument is non-optional, it still needs to have a name defined, so it can be passed by keyword or positionally (and so PyArg_ParseTupleAndKeywords can match up positions with keywords, in case the optional arguments are passed positionally). Basically, the number of keyword names must always match the maximum number of arguments to parse.

Change:

static const char *keywordList[] = { "kw1", "kw2", NULL};

to:

static const char *keywordList[] = { "input", "kw1", "kw2", NULL};

Obviously, you can name the first argument whatever you want; I just matched the C variable name.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271