About issubclass error message/behaviour

I have a weird situation here, but the question goes for the issubclass message/behaviour, see…

I have a two objects from the system I’m debugging, obj_x and obj_y.

They are probably classes:

(Pdb) inspect.isclass(obj_x)
True
(Pdb) inspect.isclass(obj_y)
True

They are not subclass of something I just create:

(Pdb) class C: pass
(Pdb) issubclass(obj_x, C)
False
(Pdb) issubclass(obj_y, C)
False

And the thing I just created is not subclass of those!

(Pdb) issubclass(C, obj_x)
False
(Pdb) issubclass(C, obj_y)
False

Now the strange thing:

(Pdb) issubclass(obj_x, obj_y)
*** TypeError: issubclass() arg 1 must be a class

What? It is a class! …or maybe not? What?

(BTW, the “arg 1” also confused me… I initially thought it was the second argument)

So, why is it saying that obj_x is not a class? What is it checking?

Shall I open a bug about this?

Thanks!

Maybe obj_x doesn’t have __bases__ properly set?

cpython does this check

if (!check_class(derived,
                     "issubclass() arg 1 must be a class"))
        return -1;

where

static int
check_class(PyObject *cls, const char *error)
{
    PyObject *bases = abstract_get_bases(cls);
    if (bases == NULL) {
        /* Do not mask errors. */
        PyThreadState *tstate = _PyThreadState_GET();
        if (!_PyErr_Occurred(tstate)) {
            _PyErr_SetString(tstate, PyExc_TypeError, error);
        }
        return 0;
    }
    Py_DECREF(bases);
    return -1;
}

Do you have any code that reliably reproduces the situation? Is there any of your own C code involved in causing the problem?

Both have __bases__.

Following the C code, can’t understand how it’s failing like this.

This is the worst part. No, I cannot reproduce it in a snippet :frowning: … big proprietary system, super complex.

dynamic programming !? :cowboy_hat_face:

Sorry, I’ll clarify. I probably could reproduce it in a snippet, but it would be a lot of work to reduce crazy-inheritance multi-modules packages to just one file.

I’m tempted, though.

Spitballing: Maybe __bases__ is not a tuple (due to failing the check for the flag below) !?

static PyObject *
abstract_get_bases(PyObject *cls)
{
    PyObject *bases;

    (void)PyObject_GetOptionalAttr(cls, &_Py_ID(__bases__), &bases);
    if (bases != NULL && !PyTuple_Check(bases)) {
        Py_DECREF(bases);
        return NULL;
    }
    return bases;
}

where

#define PyTuple_Check(op) \
                 PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_TUPLE_SUBCLASS)

I thought I had a smaller example - but mixed up my classes and instances…
On the Python level (3.11) it’s not possible to assign anything apart from tuples to the __bases__, so I suppose something must be going wrong deeper down…