microsoft / pyright

Static Type Checker for Python
Other
13.24k stars 1.43k forks source link

Aliased Annotated considering any string metadata as a type and giving errors #3853

Closed bellini666 closed 2 years ago

bellini666 commented 2 years ago

There are some libs that uses a structure similar to Annotated to add extra runtime capabilities, while still being able to keep the variables properly typed.

Strawberry is one of those libs. Since it uses the typing infrastructure for defining the graphql schema, it has a LazyType which can be used to avoid circular import issues but still be able to resolve the real type at runtime: https://github.com/strawberry-graphql/strawberry/blob/main/strawberry/lazy_type.py

I'm writing a patch for it where it would do something like this:

from typing import TYPE_CHECKING, Generic, TypeVar

from typing_extensions import Annotated

if TYPE_CHECKING:
    from decimal import Decimal

TypeName = TypeVar("TypeName")
Module = TypeVar("Module")

if TYPE_CHECKING:
    LazyType = Annotated
else:

    class LazyType(Generic[TypeName, Module]):
        ...

class Foo:
    a: Annotated["Decimal", "decimal"]
    b: LazyType["Decimal", "decimal"]

reveal_type(Foo.a)
reveal_type(Foo.b)

The idea here is that pyright would consider LazyType to be an Annotated and behave as such, but at runtime it can still do its lazy importing stuff.

If I run pyright in that file I get this:

No configuration file found.
pyproject.toml file found at /home/bellini/dev/strawberry.
Loading pyproject.toml file at /home/bellini/dev/strawberry/pyproject.toml
Assuming Python platform Linux
Searching for source files
Found 1 source file
pyright 1.1.267
/home/bellini/dev/strawberry/xxx.py
  /home/bellini/dev/strawberry/xxx.py:23:29 - error: "decimal" is not defined (reportUndefinedVariable)
  /home/bellini/dev/strawberry/xxx.py:28:13 - information: Type of "Foo.a" is "Decimal"
  /home/bellini/dev/strawberry/xxx.py:29:13 - information: Type of "Foo.b" is "Decimal"
  /home/bellini/dev/strawberry/xxx.py:30:13 - information: Type of "Foo.c" is "Decimal"
  /home/bellini/dev/strawberry/xxx.py:31:13 - information: Type of "Foo.d" is "Decimal"
1 error, 0 warnings, 4 informations 
Completed in 0.555sec

Which means that it is working as intended, but while aliased it is also trying to parse the second argument (and any other after the first) as a type if it is a string while it shouldn't (note that the issue didn't happen when I added 123).

erictraut commented 2 years ago

Yeah, this won't work. Annotated is a special form that requires a bunch of special-case logic to handle its peculiarities, and it can't be aliased like this. Sorry. You'll need to find some other approach if you want this to work with pyright.