Pylance not allowing structural subtyping with built-in types

93 views Asked by At

I'm reading the document about protocols and stuck with a Pylance error message. https://peps.python.org/pep-0544/#subtyping-relationships-with-other-types

Simplifying the code in this section, I wrote a code

from typing import Protocol, TypeVar

T = TypeVar("T", contravariant=True)
class ListLike(Protocol[T]):
    def append(self, x: T) -> None:
        ...

class MockStack:
    def append(self, x: int) -> None:
        ...

def populate(lst: ListLike[int]) -> int:
    ...

populate([1, 2, 3])
populate(MockStack())

I expect this snippet to pass type checking, since [1, 2, 3] (as list[int]) and MockStack() (as MockStack) both have the method append(self, x: int) and therefore are structural subtypes of ListLike[int].

But pylance in VS Code raises an error message:

Argument of type "list[int]" cannot be assigned to parameter "lst" of type "ListLike[int]" in function "populate"
  "list[int]" is incompatible with protocol "ListLike[int]"
    "append" is an incompatible type
      Type "(__object: int, /) -> None" cannot be assigned to type "(x: T@ListLike) -> None"
        Position-only parameter mismatch; expected 1 but received 0  (reportGeneralTypeIssues)

Could anyone give me suggestions to get around this?

environment: Apple M1 Pro, mac OS 13.5.1

VS Code version: 1.84.2

pylance version: v2023.11.10

1

There are 1 answers

1
STerliakov On

This warning is triggered by positional-only vs regular argument mismatch. Your ListLike definition permits .append(x=0) call, and so any function that requests ListLike is allowed to do such call. list.append has only positional-only argument, so is not compatible with your protocol.

You can redefine you protocol as

from typing import Protocol, TypeVar

T = TypeVar("T", contravariant=True)

class ListLike(Protocol[T]):
    def append(self, x: T, /) -> None:
        ...

to make the type checker happy.