Closed wch closed 1 week ago
Pyright's new behavior is correct. The typing spec has recently been updated to indicate how and where type checkers should use "type expression" versus "value expression" rules. In your top example, the statement ListOrTupleStr = ListOrTuple[str]
is a valid (old-style) type alias definition, and the RHS is evaluated as a type expression. In your second code sample, the statement ListOrTuple[str]
is a plain old value expression, and it's treated as such. The subexpression ListOrTuple
in this case is evaluated as its runtime type of UnionType
.
Thanks for that explanation. That part makes sense to me now.
However, the fact that the code runs means that there actually is a __getitem__
method at run time. But on the other hand, in Typeshed's stdlib/types.pyi, there is not a __getitem__
method listed there.
Does this mean that when the code is evaluated as a value expression, it uses that Typeshed stub, but when it's evaluated as a type expression, it uses something else?
Yeah, UnionType
does have a __getitem__
method (contrary to what the typeshed stub indicates), but __getitem__
raises an exception at runtime if the individual subtypes are not generic types parameterized by type variables.
from typing import TypeVar
T = TypeVar("T")
(list[T] | set[T])[0] # No runtime error
(list | set)[0] # Runtime error
So it's understandable why the typeshed definition for UnionType
doesn't declare a __getitem__
. You could try to make the case to the typeshed maintainers that a __getitem__
method should be added.
If PEP 747 is approved in its current form (or something similar), then this will be moot because the draft PEP proposes new rules for how such expressions should be evaluated type checkers.
Here's some example code that is OK:
And here is an example where pyright 1.1.370 reports errors:
Both versions of the code run in Python 3.12 without error.
I know that it's strange to evaluate
ListOrTuple[str]
at run time, but we have some existing code that did that, and pyright was OK with it before 1.1.370. Also, pyright seems to think it's OK as long as the value is saved in a variable.