Pyright: Implementation of recursive Generic Protocol does not type check

Created on 29 Dec 2020  路  4Comments  路  Source: microsoft/pyright

Describe the bug
I am trying to define a generic Node which has a parent property, guaranteed to be of the same generic Node type.

Two suggested implementations are posted below:

  • One which works in mypy but fails in pyright.
  • Another which seems correct to me but which does not type check in pyright or in mypy (however for different reasons).

Maybe I am pushing the limits of structural subtyping, but it would be very nice if this would work. Since mypy can at least handle one of my implementations I am taking the liberty to post this as a bug report here. Grateful for any input.

VS Code extension or command-line

  • pyright 1.1.97 command line.
  • mypy 0.790

Alternative 1

To Reproduce

from typing import (
    Protocol,
    TypeVar,
)

T = TypeVar("T")

class HasParent(Protocol):
    def get_parent(self: T) -> T:
        ...

GenericNode = TypeVar("GenericNode", bound=HasParent)

def generic_get_parent(n: GenericNode) -> GenericNode:
    return n.get_parent()

class ConcreteNode:
    def get_parent(self) -> "ConcreteNode":
        return self

node = ConcreteNode()
parent = generic_get_parent(node)

Expected behavior
This should type check OK. (Or should it?)

Actual behavior

Pyright does not like it:

  22:29 - error: Argument of type "ConcreteNode" cannot be assigned to parameter "n" of type "GenericNode@generic_get_parent" in function "generic_get_parent"
  聽聽Type "ConcreteNode" is not compatible with bound type "HasParent" for TypeVar "GenericNode./home/rasmus/code/boyle/alternative1.py.198"
  聽聽聽聽"get_parent" is an incompatible type
  聽聽聽聽聽聽Type "(self: ConcreteNode) -> ConcreteNode" cannot be assigned to type "(self: T@get_parent) -> T@get_parent"
  聽聽聽聽聽聽聽聽Parameter 2: type "T@get_parent" cannot be assigned to type "ConcreteNode"
  聽聽聽聽聽聽聽聽聽聽"object" is incompatible with "ConcreteNode"
  聽聽聽聽聽聽聽聽Function return type "ConcreteNode" is not compatible with type "T@get_parent"
  聽聽聽聽聽聽聽聽聽聽Type "ConcreteNode" cannot be assigned to type "T@get_parent" (reportGeneralTypeIssues)
  24:13 - info: Type of "parent" is "GenericNode@generic_get_parent"
1 error, 0 warnings, 1 info 

mypy type checks OK and correctly infers the type of parent:

alternative1.py:24: note: Revealed type is 'alternative1.ConcreteNode*'

Alternative 2

To Reproduce

from typing import (
    Protocol,
    TypeVar,
)

T = TypeVar("T")

class HasParent(Protocol[T]):
    parent: T

GenericNode = TypeVar("GenericNode", bound=HasParent["GenericNode"])

def get_parent(node: GenericNode) -> GenericNode:
    return node.parent

Expected behavior
This should type check OK. (Or should it?)

Actual behavior

pyright:

 14:12 - error: Expression of type "GenericNode" cannot be assigned to return type "GenericNode@get_parent"
  聽聽Type "GenericNode" cannot be assigned to type "GenericNode@get_parent" (reportGeneralTypeIssues)
1 error, 0 warnings, 0 infos 

mypy fails, seemingly because it does not support thbute recursive type var:

alternative2.py:11: error: Cannot resolve name "GenericNode" (possible cyclic definition)
alternative2.py:14: error: Returning Any from function declared to return "GenericNode"
Found 2 errors in 1 file (checked 1 source file)
addressed in next version bug

All 4 comments

The second example will not work. Referencing a TypeVar recursively within its own definition doesn't make sense. TypeVars must be used within a scope that gives them meaning -- a generic class, a generic method, or a generic type alias. A TypeVar declaration statement provides no such scope, so the use of a TypeVar within its own declaration won't work.

However, the first example should work. I'll look into that bug.

The bug that caused the problem with the first sample will be fixed in the next release of Pyright & Pylance.

Thanks for the swift and encouraging feedback. I also learned something from your explanation of why the second example will not work. Thank you!

This is now addressed in Pyright 1.1.98, which I just published. It will also be in the next release of Pylance.

Was this page helpful?
0 / 5 - 0 ratings