Skip to content

Emit a diagnostic on an attempt to create a "generic enum" #2416

@AlexWaygood

Description

@AlexWaygood

This class statement fails at runtime, but ty does not detect it:

from enum import Enum

class Foo[T](Enum):
    X = list[T]()

Runtime:

>>> from enum import Enum
... 
... class Foo[T](Enum):
...     X = list[T]()
...     
Traceback (most recent call last):
  File "<python-input-0>", line 3, in <module>
    class Foo[T](Enum):
        X = list[T]()
  File "<python-input-0>", line 3, in <generic parameters of Foo>
    class Foo[T](Enum):
        X = list[T]()
  File "/Users/alexw/.pyenv/versions/3.13.1/lib/python3.13/enum.py", line 491, in __prepare__
    member_type, first_enum = metacls._get_mixins_(cls, bases)
                              ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^
  File "/Users/alexw/.pyenv/versions/3.13.1/lib/python3.13/enum.py", line 956, in _get_mixins_
    raise TypeError("new enumerations should be created as "
            "`EnumName([mixin_type, ...] [data_type,] enum_type)`")
TypeError: new enumerations should be created as `EnumName([mixin_type, ...] [data_type,] enum_type)`

This also fails in the same way, and again, we emit no error:

from enum import Enum
from typing import Generic, TypeVar

T = TypeVar("T")

class Foo(Enum, Generic[T]):
    X = list[T]()

The only way you can get a "generic enum" to not immediately fail is by putting Generic[T] first in the bases list (contrary to the usual recommendation that Generic[T] should always be the last base class). But even if you do this, things do not work at runtime in the way you'd expect -- it's illegal to specialize such a class at runtime, because Generic.__class_getitem__ is totally clobbered by EnumMeta.__getitem__:

>> from enum import Enum
>>> from typing import Generic, TypeVar
>>> T = TypeVar("T")
>>> class Foo(Generic[T], Enum):
...     X = list[T]()
...     
>>> Foo[int]
Traceback (most recent call last):
  File "<python-input-5>", line 1, in <module>
    Foo[int]
    ~~~^^^^^
  File "/Users/alexw/.pyenv/versions/3.13.1/lib/python3.13/enum.py", line 789, in __getitem__
    return cls._member_map_[name]
           ~~~~~~~~~~~~~~~~^^^^^^
KeyError: <class 'int'>

Aside from whether or not it works at runtime, the concept of a generic enum just isn't very coherent on a conceptual level anyway. We currently don't permit them to exist in our internal model, and mypy/pyrefly both emit diagnostics if a user tries to create one:

We should do the same as mypy/pyrefly here.

Metadata

Metadata

Assignees

Labels

runtime semanticsAccurate modeling of how Python's semantics work at runtimetyping semanticstyping-module features, spec compliance, etc

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions