Skip to content

Incorrect narrowing of enums with custom __eq__ methods in match statements #1454

@AlexWaygood

Description

@AlexWaygood

Summary

Originally reported in https://discuss.python.org/t/amend-pep-586-to-make-enum-values-subtypes-of-literal/59456/9.

At runtime, case statements that use literal patterns or value patterns dispatch based on equality, and Color.GREEN == "g" in the following example (since Color has str in its MRO as well as Enum, and inherits its __eq__ method from str). We don't recognise this correctly in ty currently:

from enum import StrEnum
from typing import Literal, assert_never, reveal_type

class Color(StrEnum):
    RED = "r"
    GREEN = "g"
    BLUE = "b"

def test_literal_as_enum(x: Literal["g"]) -> None:
    match x:
        case Color.RED:
            assert_never(x)
        case Color.GREEN:
            reveal_type(x)  # this branch is taken at runtime
        case Color.BLUE:
            assert_never(x)
        case _:
            assert_never(x)  # false-positive error here

def test_enum_as_literal(y: Literal[Color.BLUE]) -> None:
    match y:
        case "r":
            assert_never(y)
        case "g":
            assert_never(y)
        case "b":
            reveal_type(y)  # this branch is taken at runtime
        case _:
            assert_never(y)  # false-positive error here

https://play.ty.dev/e106f66f-339a-4d1f-a6e4-95f8a4f3bed9

Version

No response

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingenumsnarrowingrelated to flow-sensitive type narrowingruntime semanticsAccurate modeling of how Python's semantics work at runtime

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions