python / python/mypy

Typealias-like definition of enum members makes mypy forget we're in an enum

Open
#14,281 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
20.6k
Forks
3.3k
PR merge metrics
PR metrics pending

Description

Bug Report

See this code

from enum import Enum
from typing import NamedTuple
from typing_extensions import reveal_type

class E(Enum):
    A = list[str]
    B = tuple[int, ...]
    C = NamedTuple("C", [("x", int), ("y", int)])

reveal_type(E.A)
reveal_type(E.A.value())
reveal_type(E.B)
reveal_type(E.B.value())
reveal_type(E.C)
reveal_type(E.C.value(0, 0))

mypy-play link: https://mypy-play.net/?mypy=latest&python=3.11&gist=24eebb55be028f1d9e21566f7b9bb27f

It seems to me that mypy thinks A = list[str] defines a type alias in the class E, but that's not what happens at runtime.

Expected Behavior

Runtime output:

Runtime type is 'E'
Runtime type is 'list'
Runtime type is 'E'
Runtime type is 'tuple'
Runtime type is 'E'
Runtime type is 'C'

Actual Behavior

mypy output:

main.py:10: note: Revealed type is "Overload(def () -> builtins.list[builtins.str], def (typing.Iterable[builtins.str]) -> builtins.list[builtins.str])"
main.py:11: error: "Type[List[Any]]" has no attribute "value"  [attr-defined]
main.py:11: note: Revealed type is "Any"
main.py:12: note: Revealed type is "def (typing.Iterable[builtins.int] =) -> builtins.tuple[builtins.int, ...]"
main.py:13: error: "Type[Tuple[Any, ...]]" has no attribute "value"  [attr-defined]
main.py:13: note: Revealed type is "Any"
main.py:14: note: Revealed type is "def (x: builtins.int, y: builtins.int) -> Tuple[builtins.int, builtins.int, fallback=enum_with_types_testcase.E.C]"
main.py:15: error: "Type[C]" has no attribute "value"  [attr-defined]
main.py:15: note: Revealed type is "Any"
Found 3 errors in 1 file (checked 1 source file)

That may be a bit hard to parse, but the gist is that mypy thinks it should work like this:

reveal_type(E.A())
reveal_type(E.B())
reveal_type(E.C(0, 0))

without the .value.

Mypy is happy with this:

enum_with_types_testcase.py:10: note: Revealed type is "builtins.list[builtins.str]"
enum_with_types_testcase.py:11: note: Revealed type is "builtins.tuple[builtins.int, ...]"
enum_with_types_testcase.py:12: note: Revealed type is "Tuple[builtins.int, builtins.int, fallback=enum_with_types_testcase.E.C]"
Success: no issues found in 1 source file

but it doesn't work at runtime.

Your Environment

See the mypy play link above.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the Python reproducer in the issue and run it through the linked mypy-play example. Compare the revealed types for E.A, E.B, and E.C with the runtime values and their .value calls; done means enum members retain enum types while their values receive the appropriate types without erroneous attribute errors.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.