microsoft / pyright

Static Type Checker for Python
Other
13.15k stars 1.41k forks source link

Regression for `Annotated` aliases when used as value expression #8916

Closed brentyi closed 1 week ago

brentyi commented 2 weeks ago

Hello!

pyright==1.1.379 seems to introduce a type error on the print statement below:

from typing import Annotated, TypeVar

T = TypeVar("T")
Const = Annotated[T, "const"]

# __getitem__ method not defined on type "Annotated"
print(Const[int])

# No error
x = Const[int]

# No error
y: Const[int] = 3

Older versions of pyright (I tried 1.1.378) don't seem to complain.

Is this expected?

Thanks!!

erictraut commented 2 weeks ago

This is a gray area, so the rules are not clear. This particular change in behavior was prompted by draft PEP 747. This is forcing us to think more deeply about how type checkers should handle special forms like Annotated or Literal in value expressions.

The statement Const = Annotated[T, "const"] defines a generic type alias named Const, and the RHS of this statement is treated as a type expression. The statement x = Const[int] defines a non-generic type alias named x, and the RHS of this statement is treated as a type expression. If you add a statement that assigns a different value to x (e.g. x = 1), then this is clearly no longer a type alias, and you'll see the same error reported for Const[int] because it is no longer evaluated as a type expression.

At runtime, Const is an instance of typing._AnnotatedAlias. This is a private class that is not documented or defined in the typeshed stubs. Unless/until it is documented and defined, it probably makes sense to special case it in type checkers and permit it to be used with __getitem__. This will be addressed in the next release.

erictraut commented 1 week ago

This is addressed in pyright 1.1.380.