Skip to content

capture_pattern binding type mismatches namedtuple type from Union #14501

Closed
@enigma

Description

@enigma

Bug Report

When matching on a union between a named tuple and something else, class/as pattern binds to a type different than the named tuple.

That is, the body of case Foo() as foo: will produce different values for reveal_type(Foo()) and reveal_type(foo).

To Reproduce

Playground link. Here for clarity:

from typing import NamedTuple
from typing_extensions import reveal_type

class Foo(NamedTuple): pass

def print_foo(foo: Foo): pass

def broken_match(some: Foo | None):
    reveal_type(some) # Union[Tuple[, fallback=__main__.Foo], None]
    match some:
        case Foo() as foo:
            # Uncommenting this works with *any* Foo definition
            #foo = some
            
            reveal_type(foo)       # __main__.Foo
            reveal_type(some)      # Tuple[, fallback=__main__.Foo]
            
            reveal_type(Foo())     # Tuple[, fallback=__main__.Foo]
            reveal_type(print_foo) # def (foo: Tuple[, fallback=__main__.Foo]) -> Any

            # Errors with: Argument 1 to "print_foo" has incompatible type "Foo"; expected "Foo"
            print_foo(foo) 
        case _:
            assert False, "nevermind"

Expected Behavior

foo should reveal whatever type Foo() reveals, and this program should type check successfully.

The program type checks if I do any of the following changes:

  • Assign foo = some inside the case body (everything is Tuple[, fallback=__main__.Foo])
  • Define Foo as plain class class Foo: pass (everything is __main__.Foo)
  • Remove the union, i.e. type the arg as some: Foo rather than some: Foo | None

Actual Behavior

main.py:15: note: Revealed type is "Union[Tuple[, fallback=__main__.Foo], None]"
main.py:21: note: Revealed type is "__main__.Foo"
main.py:22: note: Revealed type is "Tuple[, fallback=__main__.Foo]"
main.py:24: note: Revealed type is "Tuple[, fallback=__main__.Foo]"
main.py:25: note: Revealed type is "def (foo: Tuple[, fallback=__main__.Foo]) -> Any"
main.py:28: error: Argument 1 to "print_foo" has incompatible type "Foo"; expected "Foo"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

The fact it has the same name in the error is particularly confusing.

Your Environment

See playground.

  • Mypy version used: 0.991
  • Python version used: 3.11

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions