mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Support typing.Union/typing.Optional in isinstance check (/support old-style unions)

Open bram-tv opened this issue 1 year ago • 1 comments

Preamble

  • This issue may already exists but I was unable to find an exact match. There are plenty of issues related to the PEP 604 syntax but none that are really about using typing.Union/typing.Optional/...
  • Support for PEP 604 syntax was recently added in #11673 / #17371
  • Old style unions were briefly mentioned in https://github.com/python/mypy/pull/17371#pullrequestreview-2115445793

Feature

Example code

from typing import Any, Union, Optional

foo: Any = None

if isinstance(foo, int | None):
    reveal_type(foo)

if isinstance(foo, Union[int, None]):
    reveal_type(foo)

if isinstance(foo, Optional[int]):
    reveal_type(foo)

bar = int | None
baz = Optional[int]
if isinstance(foo, bar):
    reveal_type(foo)

if isinstance(foo, baz):
    reveal_type(foo)

Output for the above with mypy v1.11.1:

src/union.py:6: note: Revealed type is "Union[builtins.int, None]"
src/union.py:8: error: Argument 2 to "isinstance" has incompatible type "<typing special form>"; expected "_ClassInfo"  [arg-type]
src/union.py:9: note: Revealed type is "Any"
src/union.py:11: error: Argument 2 to "isinstance" has incompatible type "<typing special form>"; expected "_ClassInfo"  [arg-type]
src/union.py:12: note: Revealed type is "Any"
src/union.py:17: note: Revealed type is "Union[builtins.int, None]"
src/union.py:19: error: Parameterized generics cannot be used with class or instance checks  [misc]
src/union.py:19: error: Argument 2 to "isinstance" has incompatible type "<typing special form>"; expected "_ClassInfo"  [arg-type]
src/union.py:20: note: Revealed type is "Any"
Found 4 errors in 1 file (checked 1 source file)

Running the above with pyright:

.../src/union.py
  .../src/union.py:6:17 - information: Type of "foo" is "int | None"
  .../src/union.py:9:17 - information: Type of "foo" is "int | None"
  .../src/union.py:12:17 - information: Type of "foo" is "int | None"
  .../src/union.py:17:17 - information: Type of "foo" is "int | None"
  .../src/union.py:20:17 - information: Type of "foo" is "int | None"
0 errors, 0 warnings, 5 informations

Summary:

  • when using the PEP 604-syntax the type is correctly narrowed to Union[builtins.int, None]
  • when using the typing.Union and/or typing.Optional syntax the type is not narrowed and remains Any (and errors are shown)

bram-tv avatar Aug 15 '24 14:08 bram-tv

PR welcome. IIRC isinstance on old-style unions is only supported on 3.10 and newer

hauntsaninja avatar Aug 16 '24 01:08 hauntsaninja