Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enum instance type ignored in match/case statements #13841

Open
ianonavy opened this issue Oct 8, 2022 · 2 comments
Open

Enum instance type ignored in match/case statements #13841

ianonavy opened this issue Oct 8, 2022 · 2 comments
Labels

Comments

@ianonavy
Copy link

ianonavy commented Oct 8, 2022

Bug Report

When using an enum instance instead of a class property in a case statement, the type checker appears to ignore type errors inside the case statement.

May be related to #11784, not sure.

To Reproduce

import enum


class FooClass(enum.Enum):
    FOO = "FOO"


foo_inst = FooClass.FOO
match foo_inst:
    case foo_inst.FOO:
        1 + "a"

Playground link

Expected Behavior

I expected a type error on line 11, error: Unsupported operand types for + ("int" and "str"). My reasoning is that in cPython 3.10.7, FooClass.FOO and foo_inst.FOO both refer to the same object with the same id and should therefore have the same type checking behavior.

Actual Behavior

If you run mypy:

Success: no issues found in 1 source file

If you run it with python, you get a runtime TypeError even though the type checker failed to catch it.

The expected behavior occurs if you use the class name instead of the instance name:

import enum


class FooClass(enum.Enum):
    FOO = "FOO"


foo_inst = FooClass.FOO
match foo_inst:
    case FooClass.FOO:
        1 + "a"

Your Environment

  • Mypy version used: 0.982
  • Mypy command-line flags: none; same issue with --strict
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.10.7

More Details

More detailed investigation here

I tried to reveal_type to debug, and I got different values depending on the environment. With the mypy CLI against the following example and mypy Playground, I get the following:

import enum


class FooClass(enum.Enum):
    FOO = "FOO"


foo_inst = FooClass.FOO
match foo_inst:
    case FooClass.FOO:
        reveal_type(FooClass.FOO)
        reveal_type(foo_inst)
        reveal_type(foo_inst.FOO)
        1 + "a"
main.py:11: note: Revealed type is "Literal[__main__.FooClass.FOO]?"
main.py:12: note: Revealed type is "__main__.FooClass"
main.py:13: note: Revealed type is "Literal['FOO']?"
main.py:14: error: Unsupported operand types for + ("int" and "str")
Found 1 error in 1 file (checked 1 source file)

If I change line 10 to foo_inst.FOO, I get no revealed types at all: playground link.

Further, Visual Studio Code's bulit-in diagnostics seem to interpret the types for all three expressions as "Literal[FooClass.FOO]"

screenshot of Visual Studio Code showing the problems panel with messages that corroborate the above description

Curiously, the type checking seems to work when using an IntEnum instead of an Enum: playground link.

import enum


class FooClass(enum.IntEnum):
    FOO = 0


foo_inst = FooClass.FOO
match foo_inst:
    case foo_inst.FOO:
        reveal_type(FooClass.FOO)
        reveal_type(foo_inst)
        reveal_type(foo_inst.FOO)
        1 + "a"
main.py:11: note: Revealed type is "Literal[__main__.FooClass.FOO]?"
main.py:12: note: Revealed type is "<nothing>"
main.py:13: error: <nothing> has no attribute "FOO"
main.py:13: note: Revealed type is "Any"
main.py:14: error: Unsupported operand types for + ("int" and "str")
Found 2 errors in 1 file (checked 1 source file)

I also observe the expected behavior with if statements, so I think the issue is particular to the intersection of case and enum instance.

@ianonavy ianonavy added the bug mypy got something wrong label Oct 8, 2022
@AlexWaygood AlexWaygood added feature topic-enum topic-match-statement Python 3.10's match statement and removed bug mypy got something wrong labels Oct 8, 2022
@intgr
Copy link
Contributor

intgr commented Jul 27, 2023

The reason is that mypy thinks this code is unreachable, and skips typechecking of unreachable code. You can see these warnings when using --warn-unreachable option:

I think this is surprising and bad default behavior. It even ignores reveal_type() in unreachable code.

@dpinol
Copy link

dpinol commented Jul 27, 2024

A smilar case

class MyEnum(Enum):
    RAISE = 1
    ERROR = 2

    def level(self) -> int:
        match self:
            case self.RAISE:
                return 1
            case self.ERROR:
                return 2

I get

kk.py:23: error: Statement is unreachable  [unreachable]
                    return 1
                    ^~~~~~~~
kk.py:25: error: Statement is unreachable  [unreachable]
                    return 2
                    ^~~~~~~~
Found 2 errors in 1 file (checked 1 source file)

I can be workarounded doing case MyEnum.RAISE instead of case self.RAISE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants