`super` in a `typing.NamedTuple` subclass fails in python 3.8

Another workaround which seems to work for me at least is the following:

class BaseTest(typing.NamedTuple):
    a: int
    b: float
    

class Test(BaseTest):
    def __repr__(self):
        return super().__repr__()

By subclassing the NameTuple-based class, it's seems the class cell taken caer of.


I was slightly wrong in the other question (which I just updated). Apparently, this behavior manifests in both cases of super. In hindsight, I should have tested this.

What's happening here is the metaclass NamedTupleMeta indeed doesn't pass __classcell__ over to type.__new__ because it creates a namedtuple on the fly and returns that. Actually, in Python's 3.6 and 3.7 (where this is still a DeprecationWarning), the __classcell__ leaks into the class dictionary since it isn't removed by NamedTupleMeta.__new__.

class Test(NamedTuple):
    a: int
    b: float
    def __repr__(self):
        return super().__repr__()

# isn't removed by NamedTupleMeta
Test.__classcell__
<cell at 0x7f956562f618: type object at 0x5629b8a2a708>

Using object.__repr__ directly as suggested by Azat does the trick.

how can this fail at definition time

The same way the following also fails:

class Foo(metaclass=1): pass

Many checks are performed while the class is being constructed. Among these, is checking if the metaclass has passed the __classcell__ over to type_new.


Unfortunately, I'm not so familiar with CPython internals and classes generation to say why it fails, but there is this CPython bug tracker issue which seems to be related and some words in Python docs

CPython implementation detail: In CPython 3.6 and later, the __class__ cell is passed to the metaclass as a __classcell__ entry in the class namespace. If present, this must be propagated up to the type.__new__ call in order for the class to be initialised correctly. Failing to do so will result in a RuntimeError in Python 3.8.

so probably somewhere during actual namedtuple creation we have a call to type.__new__ without __classcell__ propagated, but I don't know if it's the case.

But this particular case seems to be solvable by not using super() call with explicitly saying that "we need to have __repr__ method of the object class" like

class Test(typing.NamedTuple):
    a: int
    b: float
    __repr__ = object.__repr__