Search code examples
pythonpython-c-api

Checking if an object is an int or int-like in Python C API


Is there a an operation similar to PyInt_Check/PyLong_Check that takes into account whether or not the type has an __int__ method?

The closest workaround I have been able to find so far is

int check_int(PyObject *obj)
{
    long lng;
    int over;

    lng = PyLong_AsLongAndOverflow(obj, &over);
    if(lng == -1 && over == 0 && PyErr_Occurred()) {
        PyErr_Clear();
#if PY_MAJOR_VERSION <= 2
        lng = PyInt_AsLong(obj);
        if(lng == -1L && PyErr_Occurred()) {
            PyErr_Clear();
            return 0;
        }
#else
        return 0;
#endif
    }
    return 1;
}

The problem here is that I am effectively doing something like

def isint(obj):
    try:
        int(obj)
    except TypeError:
        return False
    else:
        return True

However, this being C, I would prefer to do something like

def isint(obj):
    return isinstance(obj, int) or hasattr(type(obj), '__int__')

I would expect such a check to already exist because PyInt_AsLong and PyLong_AsLongAndOverflow already perform it. I just want to be able to know if an object might be an integer without getting the integer value at all.

That being said, I can see the point of actually getting the value, since hasattr(type(obj), '__int__') does not actually guarantee that the object can be reasonably used as an integer: e.g., if the attribute is not a function or just raises an error. In that case "no" may be a valid answer.


Solution

  • The closest thing to a function for that is PyNumber_Long, or PyNumber_Int on Python 2. Both of these functions actually perform the conversion. They will also consider methods like __trunc__, and convert strings to ints, just like calling int from Python-level code.

    If you want to check for the presence of an __int__ conversion method, you can look for the corresponding slot directly:

    if (o->ob_type->tp_as_number and o->ob_type->tp_as_number->nb_int) {
        do_whatever();
    }