microsoft / pylance-release

Documentation and issues for Pylance
Creative Commons Attribution 4.0 International
1.72k stars 766 forks source link

Pylance custom class is incompatible with original class #1094

Closed pasunx closed 3 years ago

pasunx commented 3 years ago

Environment data

Code Snippet / Additional information

class OriginalA: ...

class OriginalB:

    a: OriginalA
    b: list[OriginalA]

class CustomA(OriginalA): ...

class CustomB(OriginalB):

    def __init__(self,
            a: CustomA,
            b: list[CustomA]) -> None:

        self.a = a # work fine

        self.b = b # error:
                   #    Cannot assign member "b" for type "CustomB"
                   #      Expression of type "list[CustomA]" cannot be assigned to member "b" of class "CustomB"
                   #        TypeVar "_T@list" is invariant
                   #          "CustomA" is incompatible with "OriginalA" Pylance (reportGeneralTypeIssues)
pasunx commented 3 years ago

This seem not work with generic type

from typing import TypeVar, Generic

T = TypeVar("T")

class MyGeneric(Generic[T]): ...

class OriginalA: ...

class OriginalB:

    a: OriginalA
    b: MyGeneric[OriginalA]

class CustomA(OriginalA): ...

class CustomB(OriginalB):

    def __init__(self,
            a: CustomA,
            b: MyGeneric[CustomA]) -> None:

        self.a = a # work fine

        self.b = b # error:
                   #    Cannot assign member "b" for type "CustomB"
                   #      Expression of type "MyGeneric[CustomA]" cannot be assigned to member "b" of class "CustomB"
                   #        TypeVar "_T@MyGeneric" is invariant
                   #          "CustomA" is incompatible with "OriginalA" Pylance (reportGeneralTypeIssues)
erictraut commented 3 years ago

This is correct behavior. The type parameter for a list is invariant because it's a mutable container, so a list[CustomA] cannot be assigned to list[OriginalA].

For more details, refer to PEP 484 or this documentation.

You can assign a list[CustomA] to a Sequence[OriginalA].

    # This eliminates the type problem
    b: Sequence[OriginalA]
pasunx commented 3 years ago

so how can I solve problem if I want to use my own generic class ? eg.

from typing import TypeVar, Generic

A = TypeVar("A")
B = TypeVar("B")

class MyGeneric(Generic[A, B]):

    a: A
    b: B

    def __init__(self, a: A, b: B) -> None:
        self.a = a
        self.b = b

    def func(self):
        print("A", self.a)
        print("B", self.b)

class OriginalA: ...

class OriginalB: ...

class OriginalC:

    x: tuple[OriginalA, OriginalB]
    z: MyGeneric[OriginalA, OriginalB]

class CustomB(OriginalB): ...

class CustomC(OriginalC):

    def __init__(self,
            x: tuple[OriginalA, CustomB],
            z: MyGeneric[OriginalA, CustomB]) -> None:
        self.x = x # work fine
        self.z = z
        # error:
        #    Cannot assign member "z" for type "CustomC"
        #      Expression of type "MyGeneric[OriginalA, CustomB]" cannot be assigned to member "z" of class "CustomC"
        #        TypeVar "B@MyGeneric" is invariant
        #          "CustomB" is incompatible with "OriginalB" Pylance (reportGeneralTypeIssues)

CustomC(
    (OriginalA(), CustomB()),
    MyGeneric(OriginalA(), CustomB())
).z.func()
erictraut commented 3 years ago

The type variable needs to be marked as "covariant", like this:

B = TypeVar("B", covariant=True)
erictraut commented 3 years ago

For more details about variance types, refer to this part of PEP 483.

pasunx commented 3 years ago

oh thx