Skip to content

Restrictions on class patterns (match statement) are too strict / (possible cause: too eager to alias types) #17133

@dmoisset

Description

@dmoisset

Bug Report

Considering the following piece of code:

NestedLines = tuple

def print_lines(ls: NestedLines, indent: int = 0) -> None:
    for line in ls:
        match line:
            case NestedLines():
                print_lines(line, indent+4)
            case _:
                print(" "*indent + str(line))

print_lines((1, [2,3,4], (5, [6,7]), 8))

mypy --strict complains at the case NestedLines() saying "Class pattern class must not be a type alias with type parameters". However, given that a class pattern has to be a type object (and not a genericalias object), there's no way to resolve this that makes both the runtime and the typechecker happy.

For example, if you define

from typing import TypeAlias
NestedLines: TypeAlias = tuple["NestedLines | object"]

Running this will result in a TypeError: called match pattern must be a class.

Expected Behavior

Program should both typecheck AND print the following when run:

1
[2, 3, 4]
    5
    [6, 7]
8

Not sure what's the best solution... pyright is happy about this example, so perhaps some checks need to be removed/relaxed. In the (more complicated real usecase) code where I came accross this, the x = tuple was not intended as an alias, but there's no way to tell mypy "this is just an assignment", which I think would cmake more explicit that its usage in the match statement is not related to aliases.

The documentation says "Because the distinction between an unannotated variable and a type alias is implicit, ambiguous or incorrect type alias declarations default to defining a normal variable instead of a type alias."; I would say this case is ambiguous/incorrect as a typealias, so it should be considered a regular variable definition. But I even tried NestedLines: type = tuple, and it is still taken as an alias.

A possible desired behaviour would be: if the value used in a match statement is an "ambiguous" typealias (not defined with a TypeAlias/TypeStatement), and has actually been set to a type object (not a generic alias like tuple[Any]), do not emit an error

Actual Behavior

$ python3.12 -m mypy --strict example.py 
example.py:1: error: Missing type parameters for generic type "tuple"  [type-arg]
example.py:6: error: Class pattern class must not be a type alias with type parameters  [misc]
Found 2 errors in 1 file (checked 1 source file)

If I change it to a typealias

$ python3.12 example.py 
Traceback (most recent call last):
  File "/home/dmoissetdees/training-materials/unix/labs/whale/template/example.py", line 12, in <module>
    print_lines((1, [2,3,4], (5, [6,7]), 8))
  File "/home/dmoissetdees/training-materials/unix/labs/whale/template/example.py", line 7, in print_lines
    case NestedLines():
         ^^^^^^^^^^^^^
TypeError: called match pattern must be a class

And mypy still complains (in this case I think correctly):

example.py:7: error: Class pattern class must not be a type alias with type parameters  [misc]

Your Environment

  • Mypy version used: mypy 1.8.0 (compiled: yes)
  • Mypy command-line flags: --strict
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.12.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions