← board

The sequence protocol does not yield iteration

The repro

class Seq:
    def __init__(self):
        self._items = ["a", "b", "c"]

    def __len__(self):
        return len(self._items)

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


s = Seq()
print(len(s), s[0], s[-1])   # 3 a c        -- correct in both
for x in s:                  # CPython: a b c
    print("iter", x)
print("list", list(s))       # CPython: ['a', 'b', 'c']

CPython falls back to the old sequence protocol: with no __iter__, it calls __getitem__ with 0, 1, 2, … until IndexError. This is not a legacy curiosity — it is how a great deal of real code, including CPython's own pure-Python xml.etree.ElementTree.Element, becomes iterable.

Two symptoms, and the quiet one is the problem

shape pxx
len(s), s[0], s[-1] correct
for x in s: compile error: pascal26:14: error: Nil Python: pylib (count) not loaded
list(s) compiles, prints []

The compile error is survivable — loud, early, at the right line — though its text names an internal (pylib (count)) that has nothing to do with the source, so it sends the reader looking in the wrong place. Worth fixing on its own even if the fallback is not implemented.

list(s) is the real defect: it compiles clean and answers an empty list for a three-element sequence. Nothing raises, and every downstream len(), sum, or join reads as "there was nothing there". That is the plausible-wrong-value shape from devdocs/dev/root-cause-over-microfix.md. Even if the protocol fallback is judged out of scope, list() over an object it cannot iterate must fail rather than return [].

The double case to check before closing

devdocs/dev/normalise-dont-special-case.md: iteration is reachable through at least for, list(), tuple(), unpacking, in, and the comprehension forms. for errors and list() silently empties, which is already two different answers to one question — so grep the sibling arms rather than fixing the two observed here.

Not blocking

lib/rtl/mimic_xml_etree_elementtree.py defines __iter__ explicitly (returning iter(self._children)). That is not registered as a workaround: CPython's C Element carries tp_iter too, so the behaviour is identical either way and the spelling is a fair one to keep after this is fixed. The comment at that method points here.

Gate

Track N: the repro's for loop and list() both give a b c / ['a', 'b', 'c'], make test-nilpy green, self-host byte-identical. The regression test must assert the contents of list(s), not just that it compiles — a test checking only compilation passed the entire time the answer was [].


Superseded 2026-08-19 — closed as a duplicate, not as work

Filed from Track B while writing the ElementTree shim. Track N had reached the same ground independently and split it in two, and both halves were resolved the same day, before this ticket was ever dispatched:

So nothing here is unfixed and nothing is lost — this file is moved out of the queue only so it cannot dispatch a third agent onto work that is already done. Closed as a duplicate rather than resolved, because the fix did not land under this ticket and the record should say who did it.