Skip to content

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

Closed

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)

Activity

  1. hauntsaninja commented on May 24, 2026

    @hauntsaninja
    Collaborator

    The general issue is discussed in #7813. Fully general support for old-style iterables needs to be done in type checkers, not typeshed. That said, we can do something for the calendar.day_name case.

    If you need an unblock, an extra iter will fix:

    for index, day_name in enumerate(iter(day_name)):
        print(index, day_name)
    
  2. added 4 commits that reference this issue on May 24, 2026
    76f0123
    f162dab
    e072a36
    23246f8
  3. DetachHead commented on May 24, 2026

    @DetachHead
    ContributorAuthor

    Thanks, though I dont really understand the reasoning for not fixing the underlying issue. Yes, pretty much every usage of Iterable would need to be updated, but it's not like this is a disruptive breaking change. it makes inputs more lenient, not less. So it would only remove false positive errors from user code. In fact we don't even need to update all of them at once, it could be a gradual migration if that's preferred.

    I also highly disagree with the idea that it should be special cases by type checkers. As the maintainer of a type checker myself, I think special casing is one of the worst parts of Python's type system. It leads to more maintenance burden and far more confusion for users.

  4. JelleZijlstra commented on May 25, 2026

    @JelleZijlstra
    Member
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions