Skip to content

Problems with narrowing generic T | Sequence[T] #19202

Closed as duplicate of#15151
Closed as duplicate of#15151
@randolf-scholz

Description

@randolf-scholz

Trying to narrow T | Sequence[T] with isinstance clauses leads to type errors. I believe this has to do with the example below actually wanting T | (Sequence[T] & ¬T).

So in the first clause, I guess mypy thinks this could be either L & Foo = Foo or Sequence[L] & Foo, which is not necessarily an instance of L. (so maybe not a bug but nevertheless a serious divergence between type checkers).

To Reproduce

from collections.abc import Sequence

class Foo: ...

def concat[L: Foo, R: Foo](
    left: L | Sequence[L],
    right: R | Sequence[R], /
) -> list[L | R]:
    match left, right:
        case Foo(), Foo():
            return list( (left, right) )  # ❌
        case Foo(), [*rvalues]:
            return list( (left, *rvalues) )  # ❌
        case [*lvalues], Foo():
            return list( (*lvalues, right) )  # ❌
        case [*lvalues], [*rvalues]:
            return list( (*lvalues, *rvalues) )
        case _:
            raise TypeError
            
def concat2[L: Foo, R: Foo](
    left: L | Sequence[L],
    right: R | Sequence[R], /
) -> list[L | R]:
    if isinstance(left, Foo) and isinstance(right, Foo):
        return list( (left, right) )  # ❌
    elif isinstance(left, Foo) and isinstance(right, Sequence):
        return list( (left, *right) )  # ❌
    elif isinstance(left, Sequence) and isinstance(right, Foo):
        return list( (*left, right) )  # ❌
    elif isinstance(left, Sequence) and isinstance(right, Sequence):
        return list( (*left, *right) )
    else:
        raise TypeError

Expected Behavior

This code passes without issues in pyright-playground and ty-playground

Actual Behavior

mypy-playground emits 6 errors

main.py:14: error: Argument 1 to "SeqFoo" has incompatible type "tuple[Foo, Foo]"; expected "Iterable[L | R]"  [arg-type]
main.py:16: error: Argument 1 to <tuple> has incompatible type "Foo"; expected "L | R"  [arg-type]
main.py:18: error: Argument 2 to <tuple> has incompatible type "Foo"; expected "L | R"  [arg-type]
main.py:29: error: Argument 1 to "SeqFoo" has incompatible type "tuple[Foo, Foo]"; expected "Iterable[L | R]"  [arg-type]
main.py:31: error: Argument 1 to <tuple> has incompatible type "Foo"; expected "L | R"  [arg-type]
main.py:33: error: Argument 2 to <tuple> has incompatible type "Foo"; expected "L | R"  [arg-type]
Found 6 errors in 1 file (checked 1 source file)

At the very least, the error messages are misleading, since the problematic bit are the arguments to SeqFoo, not to tuple.

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