From 02c102da885922906aa57a0468fde909cbbd2ba4 Mon Sep 17 00:00:00 2001 From: David Peter Date: Thu, 20 Nov 2025 12:59:36 +0100 Subject: [PATCH] [ty] Add tests: `types.UnionType` in `isinstance`/`issubclass` (#21537) ## Summary Add some tests documenting the fact that we don't support `types.UnionType` in `isinstance`/`issubclass` at the moment. --- .../resources/mdtest/narrow/isinstance.md | 27 +++++++++++++++++++ .../resources/mdtest/narrow/issubclass.md | 27 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/crates/ty_python_semantic/resources/mdtest/narrow/isinstance.md b/crates/ty_python_semantic/resources/mdtest/narrow/isinstance.md index a931b2c367..14aab2ff8e 100644 --- a/crates/ty_python_semantic/resources/mdtest/narrow/isinstance.md +++ b/crates/ty_python_semantic/resources/mdtest/narrow/isinstance.md @@ -147,6 +147,33 @@ def _(x: int | str | bytes): reveal_type(x) # revealed: (int & Unknown) | (str & Unknown) | (bytes & Unknown) ``` +## `classinfo` is a `types.UnionType` + +Python 3.10 added the ability to use `Union[int, str]` as the second argument to `isinstance()`: + +```py +from typing import Union + +IntOrStr = Union[int, str] + +reveal_type(IntOrStr) # revealed: types.UnionType + +def _(x: int | str | bytes | memoryview | range): + # TODO: no error + # error: [invalid-argument-type] + if isinstance(x, IntOrStr): + # TODO: Should be `int | str` + reveal_type(x) # revealed: int | str | bytes | memoryview[int] | range + # TODO: no error + # error: [invalid-argument-type] + elif isinstance(x, Union[bytes, memoryview]): + # TODO: Should be `bytes | memoryview[int]` + reveal_type(x) # revealed: int | str | bytes | memoryview[int] | range + else: + # TODO: Should be `range` + reveal_type(x) # revealed: int | str | bytes | memoryview[int] | range +``` + ## `classinfo` is a `typing.py` special form Certain special forms in `typing.py` are aliases to classes elsewhere in the standard library; these diff --git a/crates/ty_python_semantic/resources/mdtest/narrow/issubclass.md b/crates/ty_python_semantic/resources/mdtest/narrow/issubclass.md index 8fa1f54963..a95905d42c 100644 --- a/crates/ty_python_semantic/resources/mdtest/narrow/issubclass.md +++ b/crates/ty_python_semantic/resources/mdtest/narrow/issubclass.md @@ -200,6 +200,33 @@ def _(x: type[int | str | bytes]): reveal_type(x) # revealed: (type[int] & Unknown) | (type[str] & Unknown) | (type[bytes] & Unknown) ``` +## `classinfo` is a `types.UnionType` + +Python 3.10 added the ability to use `Union[int, str]` as the second argument to `issubclass()`: + +```py +from typing import Union + +IntOrStr = Union[int, str] + +reveal_type(IntOrStr) # revealed: types.UnionType + +def f(x: type[int | str | bytes | range]): + # TODO: No error + # error: [invalid-argument-type] + if issubclass(x, IntOrStr): + # TODO: Should be `type[int] | type[str]` + reveal_type(x) # revealed: type[int] | type[str] | type[bytes] | + # TODO: No error + # error: [invalid-argument-type] + elif issubclass(x, Union[bytes, memoryview]): + # TODO: Should be `type[bytes]` + reveal_type(x) # revealed: type[int] | type[str] | type[bytes] | + else: + # TODO: Should be `` + reveal_type(x) # revealed: type[int] | type[str] | type[bytes] | +``` + ## Special cases ### Emit a diagnostic if the first argument is of wrong type