typeshed icon indicating copy to clipboard operation
typeshed copied to clipboard

Use positional only arguments for dunder methods in collections.abc

Open randolf-scholz opened this issue 7 months ago • 3 comments

Currently, some dunder methods like Sequence.__getitem__ do not use positional-only arguments, which causes errors when trying to subclass with positional-only arguments. Therefore, dunder methods, especially operators like __getitem__, that are typically not called using keyword arguments, should probably be annotated using positional only arguments in the stubs.

Code sample in pyright playground

from typing import Protocol, Self, overload, Literal

class SupportsSequenceGetitemA[T](Protocol):
    @overload
    def __getitem__(self, index: int) -> T: ...
    @overload
    def __getitem__(self, index: slice) -> "SupportsSequenceGetitemA[T]": ...

class MySequenceA[T](SupportsSequenceGetitemA[T], Protocol):
    @overload
    def __getitem__(self, index: int, /) -> T: ...
    @overload
    def __getitem__(self, index: slice, /) -> Self: ...  # ❌

class SupportsSequenceGetitemB[T](Protocol):
    @overload
    def __getitem__(self, index: int, /) -> T: ...
    @overload
    def __getitem__(self, index: slice, /) -> "SupportsSequenceGetitemB[T]": ...

class MySequenceB[T](SupportsSequenceGetitemB[T], Protocol):
    @overload
    def __getitem__(self, index: int, /) -> T: ...
    @overload
    def __getitem__(self, index: slice, /) -> Self: ...  # ✅

randolf-scholz avatar May 15 '25 08:05 randolf-scholz

While I do agree that these should be positional only, this should be done in CPython first. If the Python maintainers agree that this is a good idea, we can implement it in typeshed – and I'd be okay to do this for all Python versions in typeshed.

srittau avatar May 15 '25 09:05 srittau

This is different with mypy. In mypy, dunder methods are implicitly positional-only, and your example type-checks without errors as is. I'm tempted to say that mypy is correct here, and this is a pyright problem, but I don't feel very strongly about it.

Akuli avatar May 15 '25 09:05 Akuli

I opened https://github.com/python/cpython/issues/135312.

However, I do want to point out that the current stubs can actually lead to some false negatives. For instance, list does not allow calling __getitem__ with index as a keyword. Code sample in pyright playground

from collections.abc import Sequence

def get(x: Sequence[int], index: int) -> int:
    return x.__getitem__(index=index)

get([1,2,3], 0)  # TypeError at runtime

Also, I noticed that the stubs already use positional-only sometimes when CPython doesn't, for instance with Container.__contains__.

randolf-scholz avatar Jun 09 '25 22:06 randolf-scholz