microsoft / pyright

Static Type Checker for Python

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

TypeVar using old syntax falsely identified as not a TypeVar in very specific situation

ImogenBits opened this issue · comments

Not sure how to even describe the root cause of this, but I've found some incredibly strange behaviour when using the old TypVar syntax with weird bounds. This is a minimal working example to see the bug:

def early_reference():
    Second


T = TypeVar("T", bound="All")


class First(Generic[T]): # pyright error: Type argument for "Generic" must be a type variable
    attr: T # pyright error: Type variable "T" has no meaning in this context


class Second(Generic[T]):
    attr: T


All = First | Second

It seems that some part of the type var machinery breaks when you use the old syntax to define a TypeVar, it is bound to a union of types that use it, and one of those types is used above the TypeVar definition. The error occurs on all classes except the first one to be referenced above the TypeVar line. E.g. if you replace the body of early_reference in the example with references to both First and Second the error will move depending on which is first.

I'm using the VSCode extension and pyright 1.1.362

You have created a circular reference. Pyright's lazy evaluator is designed to be able to evaluate the type of any expression at any time, and it recursively evaluates types of symbols until it's able to answer the initial request. However, if there is a true circular dependency between symbols, pyright will not be able to deterministically evaluate types.

In your case, the type of class First depends on TypeVar T which is bound to All which depends on class First.

This is effectively a duplicate of #6009, where a similar situation occurs with the default value of a TypeVar rather than the bound.

I've looked for ways to break this circular dependency in a deterministic way, for example by deferring evaluation of the upper bound. This turns out to be really difficult.

I've left bug #6009 open because I would like to fix this eventually, but it's not something that is likely to be fixed anytime soon.

I'm going to close this issue as a duplicate.