Why doesn't super() work with static methods other than __new__?

super() with no arguments in Python 3 is basically a hack over its argument based version.

When super() gets no arguments it fetches the first argument i.e the class using a special cell variable named __class__ and for second argument it will get the first local variable from the stack(which is going to be function's first argument).

In case of __new__ it can get both(__class__ and cls) and works fine.

But in this case for example there's no second variable available apart from __class__ hence it fails.

class A:
  @staticmethod
  def func():
    super().func()  # super(__class__, <missing>).func()


A().func()  # RuntimeError: super(): no arguments

Now if we change it to accept an argument then things change:

class A:
  @staticmethod
  def func(foo):
    super().func()


# This fails because super(B, 1).func() doesn't make sense.
A().func(1)  # TypeError: super(type, obj): obj must be an instance or subtype of type
# Works! But as there's no parent to this class with func() it fails as expected.
A().func(A())  # AttributeError: 'super' object has no attribute 'func'

Hence the only solution is to make the things explicit with super() in your case:

super(C, C).funcC()

In general I am not sure why the implementation in case of staticmethod can't make an exception and use __class__ for both the arguments to make it work.


Related CPython code:

static int
super_init(PyObject *self, PyObject *args, PyObject *kwds)
{
    superobject *su = (superobject *)self;
    PyTypeObject *type = NULL;
    PyObject *obj = NULL;
    PyTypeObject *obj_type = NULL;

    if (!_PyArg_NoKeywords("super", kwds))
        return -1;
    if (!PyArg_ParseTuple(args, "|O!O:super", &PyType_Type, &type, &obj))
        return -1;

    if (type == NULL) {
        /* Call super(), without args -- fill in from __class__
           and first local variable on the stack. */
        PyFrameObject *f;
        PyCodeObject *co;
        Py_ssize_t i, n;
        f = PyThreadState_GET()->frame;
        if (f == NULL) {
            PyErr_SetString(PyExc_RuntimeError,
                            "super(): no current frame");
            return -1;
        }
        co = f->f_code;
        if (co == NULL) {
            PyErr_SetString(PyExc_RuntimeError,
                            "super(): no code object");
            return -1;
        }
        if (co->co_argcount == 0) {
            PyErr_SetString(PyExc_RuntimeError,
                            "super(): no arguments");
            return -1;
        }
        ...