Skip to content

And of isinstances / typeguards containing unions #14434

Description

@Anonymous-Stranger

Bug Report

When taking the and of multiple isinstances/typeguards, it seems like only the most recent is used to constrain the variable whose type is being checked.

To Reproduce

See Gist: https://gist.github.com/a4cc96c4bb9207a3ae6a344016d2e46d

from typing import Any, TypeGuard, reveal_type

def int_or_str(x: Any) -> TypeGuard[int | str]:
    return isinstance(x, int | str)

def int_or_list(x: Any) -> TypeGuard[int | list]:
    return isinstance(x, int | list)

def working_in_pyright(x: Any) -> TypeGuard[int]:
    if isinstance(x, int | str) and isinstance(x, int | list):
        reveal_type(x)  # pyright: int, mypy: int | list
        return True
    return False

def failing_in_both(x: Any) -> TypeGuard[int]:
    if int_or_str(x) and int_or_list(x):
        reveal_type(x)  # int | list
        return True
    return False

# incidentally, changing `and` to `or` results in the correct output `int | str | list`

Expected Behavior

I think the code reveal_type(x) should return int in each of these examples.

I'm not sure about the failing_in_both example: I vaguely understand that user-defined type guards do not constrain the negative case of an if (but I haven't yet understood the rationale) -- does similar reasoning explain why the type guards on the LHS and RHS of the and do not combine?

I mean, technically, I could write a TypeGuard that modifies the argument, and so I guess the LHS of the and could cease to hold when the RHS is called; is this why? Would @erictraut's StrictTypeGuard python/typing#1013 be able to resolve this?

Actual Behavior

main.py:14: note: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]"
main.py:21: note: Revealed type is "Union[builtins.int, builtins.str, builtins.list[Any]]"
Success: no issues found in 1 source file

Your Environment

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

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions