LibCST
LibCST copied to clipboard
pyright + `__or__` in generic type annotations gives unknown type
pyright gets confused by your custom __or__
methods on matcher classes, giving "Expected type expression but received UnionType
". It does work with mypy, though I suspect that might simply be because mypy doesn't try to parse it for type hints.
It's possible to work around it by using Union
in types, but that's on its way out & inconvenient.
repro
from __future__ import annotations
from libcst.matchers import Name
from typing_extensions import reveal_type
# Name.__or__ is broken
l1: list[Name|int] = [Name("foo"), 5]
reveal_type(l1)
# int.__or__ works, but if you want to union multiple matcher types it's not going to help
l2: list[int|Name] = [Name("foo"), 5]
reveal_type(l2)
$ pyright foo.py
./foo.py
./foo.py:8:1 - error: Type of "l1" is partially unknown
Type of "l1" is "list[Unknown]" (reportUnknownVariableType)
./foo.py:8:10 - error: Expected type expression but received "UnionType" (reportGeneralTypeIssues)
./foo.py:9:13 - information: Type of "l1" is "list[Unknown]"
./foo.py:11:13 - information: Type of "l2" is "list[int | Name]"
2 errors, 0 warnings, 2 informations
$ mypy foo.py
foo.py:7: note: Revealed type is "builtins.list[Union[libcst.matchers.Name, builtins.int]]"
foo.py:9: note: Revealed type is "builtins.list[Union[builtins.int, libcst.matchers.Name]]"
Success: no issues found in 1 source file
versions
libcst 1.3.1 pyright 1.1.362 mypy 1.9.0
I think Pyright's behavior is correct here; essentially we're running into the incompatible changes section in PEP 604.
Other than changing the matcher syntax to use something other than |
to delineate alternatives, the only other way I can see us resolving this is if we make types.UnionType
work with matchers the same way as TypeOf
does - then we could remove this override
https://github.com/Instagram/LibCST/blob/6783244eab6f38bf456cd60090201c31c5ec9357/libcst/matchers/_matcher_base.py#L73-L74
which I believe is causing the problem.
We also override __or__
to produce OneOf
matchers, but these are generally instance method overrides, so I don't think they would produce type errors like above