How does PyArg_ParseTupleAndKeywords work?

Have you read through the opening explanation at http://docs.python.org/c-api/arg.html ? It does a pretty good job of explaining what's going on. Don't go right to the specific reference for PyArg_ParseTupleAndKeywords; it assumes you read the text above, and isn't very helpful by itself.

You've almost got it, though. The first argument is indeed a list of incoming positional arguments. The second is a map of incoming keyword arguments (mapping given keyword name to given value). The fourth argument is actually the list of keywords which your function is prepared to accept. Yes, the 3rd argument is the format string and the 5th and later are C pointers into which the values are copied.

You'll find PyArg_ParseTupleAndKeywords() under Python/getargs.c.


To emulate the following in python:

def keywords(a, b, foo=None, bar=None, baz=None):
    pass

The following will work:

static PyObject *keywords(PyObject *self, PyObject *args, PyObject *kwargs)
{
    char *a;
    char *b;
    char *foo = NULL;
    char *bar = NULL;
    char *baz = NULL;

    // Note how "a" and "b" are included in this
    // even though they aren't supposed to be in kwargs like in python
    static char *kwlist[] = {"a", "b", "foo", "bar", "baz", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss|sss", kwlist, 
                                     &a, &b, &foo, &bar, &baz)) 
    {
        return NULL;
    }

    printf("a is %s\n", a);
    printf("b is %s\n", b);
    printf("foo is %s\n", foo);
    printf("bar is %s\n", bar);
    printf("baz is %s\n", baz);

    Py_RETURN_NONE;
}

// ...

static PyMethodDef SpamMethods[] = 
{
    // ...
    {"keywords", (PyCFunction) keywords, METH_VARARGS | METH_KEYWORDS, "practice kwargs"},
    {NULL, NULL, 0, NULL}
    // ...
}

And to use it:

from spam import keywords

keywords()         # Fails, require a and b
keywords('a')      # fails, requires b
keywords('a', 'b')
keywords('a', 'b', foo='foo', bar='bar', baz='baz') 
keywords('a', 'b','foo', 'bar', 'baz')
keywords(a='a', b='b', foo='foo', bar='bar', baz='baz')