Skip to content

constructors for builtin iterables such as list should accept objects with __getitem__(self, i: int, /) #15826

@DetachHead

Description

@DetachHead

Code sample in basedpyright playground

from collections.abc import Sequence


class Foo:
    def __init__(self, value: Sequence[int]) -> None:
        self.value = value

    def __getitem__(self, i: int):
        return self.value[i]


foo = Foo((1, 2, 3))

# works at runtime
print(list(foo))  # pyright error: "Foo" is incompatible with protocol "Iterable[_T@list]"

# works at runtime
for index, value in enumerate(foo):  # pyright error: "Foo" is incompatible with protocol "Iterable[_T@list]"
    print(f"{index=}, {value=}")

# works at runtime. objects with `__getitem__` are iterable (as long as `__getitem__` accepts `int`s)
for value in foo:
    print(value)

i'm not sure if there's an existing protocol just for __getitem__, but if not, there probably should be. perhaps something like this:

class HasGetItem[K, V](Protocol):
    def __getitem__(self, index: K) -> V: ...

type AnyIterable[T] = Iterable[T] | HasGetItem[int, T]

real world example

i bumped pyright to 1.1.410 and now it reports errors on code that enumerates calendar.day_name, due to #15738:

from calendar import day_name

for index, day_name in enumerate(day_name):
    print(index, day_name)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions