Search code examples
pythoncpython-2.xcpython

Parsing unsigned ints (uint32_t) in python's C api


If I write a function accepting a single unsigned integer (0 - 0xFFFFFFFF), I can use:

uint32_t myInt;
if(!PyArg_ParseTuple(args, "I", &myInt))
    return NULL;

And then from python, I can pass an int or long.

But what if I get passed a list of integers?

uint32_t* myInts;
PyObject* pyMyInts;
PyArg_ParseTuple(args, "O", &pyMyInts);

if (PyList_Check(intsObj)) {
    size_t n = PyList_Size(v);
    myInts = calloc(n, sizeof(*myInts));

    for(size_t i = 0; i < n; i++) {
        PyObject* item = PyList_GetItem(pyMyInts, i);

        // What function do I want here?
        if(!GetAUInt(item, &myInts[i]))
            return NULL;
    }
}

// cleanup calloc'd array on exit, etc

Specifically, my issue is with dealing with:

  • Lists containing a mixture of ints and longs
  • detecting overflow when assigning to the the uint32

Solution

  • You could create a tuple and use the same method you used for a single argument. On the C side, the tuple objects are not really immutable, so it wouldn't be to much trouble.

    Also PyLong_AsUnsignedLong could work for you. It accepts int and long objects and raises an error otherwise. But if sizeof(long) is bigger than 4, you might need to check for an upper-bound overflow yourself.

    static int 
    GetAUInt(PyObject *pylong, uint32_t *myint) {
        static unsigned long MAX = 0xffffffff;
        unsigned long l = PyLong_AsUnsignedLong(pylong);
    
        if (l == -1 && PyErr_Occurred() || l > MAX) {
            PyErr_SetString(PyExc_OverflowError, "can't convert to uint32_t");
            return false;
        }
    
        *myint = (uint32_t) l;
        return true;
    }