Skip to content

Conversation

@sterliakov
Copy link
Collaborator

@sterliakov sterliakov commented Oct 30, 2025

As discovered by @randolf-scholz in #20142, mypy treats value patterns in match statement in a completely wrong way.

From PEP 622:

Literal pattern uses equality with literal on the right hand side, so that in the above example number == 0 and then possibly number == 1, etc will be evaluated.

Existing tests for the feature are invalid: for example,

foo: object

match foo:
    case 1:
        reveal_type(foo)

must reveal object, and test testMatchValuePatternNarrows asserts the opposite. Here's a runtime example:

>>> class A:
...     def __eq__(self,o): return True
...     
>>> match A():
...     case 1:
...         print("eq")
...         
eq

I have updated the existing tests accordingly.

The idea is that value patterns are essentially equivalent to if foo == SomeValue checks, not isinstance checks modelled by conditional_types_wit_intersection.

The original implementation was introduced in #10191.

@github-actions

This comment has been minimized.

@sterliakov
Copy link
Collaborator Author

I'm frankly not sure what part of the core team to ping here - reviewers of the original PR, @JukkaL? @randolf-scholz please also take a look if you have time, IIRC some recent PRs of yours were related to pattern matching?

Primer finds nothing, apparently match isn't that popular in wild...

@sterliakov sterliakov marked this pull request as ready for review October 30, 2025 14:30
@randolf-scholz
Copy link
Contributor

Primer finds nothing, apparently match isn't that popular in wild...

Well, many popular projects still support 3.9, but match-case requires at least 3.10, so that's not surprising.

@randolf-scholz
Copy link
Contributor

randolf-scholz commented Oct 30, 2025

One thing that might be worth testing is if it interacts correctly with unions types and union patterns, e.g.

from typing import Literal

def test1(x: Literal[1,2,3]) -> None:
    match x:
        case 1:
            reveal_type(x)
        case other:
            reveal_type(x)

def test2(x: Literal[1,2,3]) -> None:
    match x:
        case 1:
            reveal_type(x)
        case 2:
            reveal_type(x)
        case 3:
            reveal_type(x)
        case other:
            assert_never(x)

def test3(x: Literal[1,2,3]) -> None:
    match x:
        case 1 | 3:
            reveal_type(x)
        case other:
            reveal_type(x)

and possibly the same with enum arguments / enum patterns.

@sterliakov
Copy link
Collaborator Author

I don't want to add any enum tests here, they won't be representative due to #19594 - I'd prefer to add match equivalents to tests there once this PR is merged. Unions are more relevant, thanks!

@github-actions
Copy link
Contributor

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants