Type object '<cls>' has no attribute '__parameters__'

class BaseStructureError(Exception):
    def __init_subclass__(cls) -> None:
        if cls is BaseStructureError:
            raise RuntimeError

class StructureFoundError[T: object = object](BaseStructureError):
    def __init__(self, structure: T, *args: object) -> None:
        self.structure = structure
        super().__init__(structure, *args)

class SynAlreadyTakenError[T: object = object](StructureFoundError[T]): pass

Why does this code give me the following error:

File "...\errors.py", line 17, in <generic parameters of SynAlreadyTakenError>
    class SynAlreadyTakenError[T: object = object](StructureFoundError[T]): pass
                                                   ~~~~~~~~~~~~~~~~~~~^^^
  File "...\typing.py", line 399, in inner
    return _caches[func](*args, **kwds)
           ~~~~~~~~~~~~~^^^^^^^^^^^^^^^
  File "...\typing.py", line 1143, in _generic_class_getitem
    for param in cls.__parameters__:
                 ^^^^^^^^^^^^^^^^^^
AttributeError: type object 'StructureFoundError' has no attribute '__parameters__'

Is this an actual Python interpreter bug or am I missing something obvious?

In PEP 695 nothing was said about class cls[T: x = x](): … being valid syntax, so it has not been implemented in any way. Therefore the error likely stems from there.

Sadly, instead of raising a SyntaxError there, it just continue, but does not set __parameters__, which means that typing’s internals cannot parameterize your generic.

Therefore I suppose this is a bug.

For context, I used Python 3.14rc3 in my example but just tried on Python 3.14.0 and the same issue occurs. I mean PEP 696 specified type defaults for type parameters, so this SHOULD work from my knowledge.

1 Like

Alright I found the bug.

Old code (simplified):

class Base:
    def __init_subclass__(cls) -> None:
        pass

class Sub[T](Base): pass

class SubSub(Sub[object]): pass

New code:

class Base:
    def __init_subclass__(cls) -> None:
        super().__init_subclass__()

class Sub[T](Base): pass

class SubSub(Sub[object]): pass

In a nutshell, I forgot to call super().__init_subclass__. I didn’t really expect that to be an issue because I thought it was just a simple hook you can use optionally, but apparently not.

1 Like

Oh, seems.like.i.missed that PEP.

1 Like

This error message is pretty unfortunate. I’d be open to a PR to enhance the error message here, probably suggesting that if the .__parameters__ attribute does not exist, you probably forgot to call super().__init_subclass__().

I opened Better error message if `Generic.__init_subclass__` is not called · Issue #139905 · python/cpython · GitHub to track improving the error message.

4 Likes