micropython icon indicating copy to clipboard operation
micropython copied to clipboard

Two argument form of iter() is not implemented.

Open hardkrash opened this issue 6 years ago • 7 comments

The builtin iter(callable, sentinel) from pep 234 is not implemented.

The use case is something like this.

with open('file.bin', 'rb') as f:
    for chunk in iter(lambda: f.read(128), b''):
        process_chunk(chunk)

The two parameter form allows an easy way to make a callable into an iterable by using a sentinel.

It allows for replacing the do while workarounds with a simple iterator.

with open('file.bin', 'rb') as f:
    chunk = f.read(128)
    while chunk != b'':
        process_chunk(chunk)
        chunk = f.read(128)

Credit to this stack overflow answer from jfs https://stackoverflow.com/a/20014805

hardkrash avatar Dec 05 '19 07:12 hardkrash

Sort of related to #4400.

mikewadsten avatar Dec 05 '19 17:12 mikewadsten

Yes, this is simply not implemented, mainly because the same thing can be achieved in pure Python. As a workaround the following can be used:

def iter2(c, s):
    while True:
        v = c()
        if v == s:
            raise StopIteration
        yield v

(This would be simpler with the walrus operator!)

dpgeorge avatar Dec 06 '19 03:12 dpgeorge

While I agree on the fact that this can be worked around in many ways. it is unfortunate that this is not explicitly called out in the documentation as a difference from python's core syntax.

This would be a compatibility wrapper needed to make the two argument form of iter work without changing source code.

from compat import iter

Where compat.py has something like this.

S = some_singleton
_orig_iter = iter
def _compat_iter(c, s=S):
    if s is S:
        for x in _orig_iter(c):
            yield x
    else:
        while True:
            v = c()
            if v == s:
                raise StopIteration
            yield v

iter = _compat_iter

Would adding 2 argument form to the builtin iter function be an acceptable patch?

hardkrash avatar Dec 17 '19 22:12 hardkrash

it is unfortunate that this is not explicitly called out in the documentation as a difference from python's core syntax.

It's not really a syntax difference, rather the fact that MicroPython implements a subset of the Python builtins. It could be added to the docs via tests/cpydiff (which automatically generates differences for the docs).

Would adding 2 argument form to the builtin iter function be an acceptable patch?

Implemented in C I think it'd be a large patch and therefore unlikely to be made part of default MicroPython builds. It could be made optional like the 2-arg form of next(), but I'm just not sure how generally useful it would be (it hasn't been requested so far).

dpgeorge avatar Dec 18 '19 05:12 dpgeorge

This would be a good candidate for #5025.

dpgeorge avatar Oct 20 '21 01:10 dpgeorge

Found this not implemented the hard way. next called with two params can just throw Not implemented exception would be much easier to understand than

TypeError: function takes 1 positional arguments but 2 were given

hex007 avatar Apr 19 '23 14:04 hex007

Labelling as docs, as I think the most sensible to do in the short term is to add this to the documented list of CPython differences.

projectgus avatar Aug 28 '24 04:08 projectgus

I was also surprised to discover next not taking a second default/sentinel parameter - and not finding any mention of this in https://docs.micropython.org/en/latest/genrst/index.html#micropython-differences-from-cpython

Naively, I would have expected to see a section in the docs on differences in "builtin methods" above/below the section on differences in "builtin types".

Image

Again naively, this breaks my assumptions about MicroPython a bit because it seems surprising that the logic to support the second parameter would increase the size noticeably, nor present any performance cost. Yes, it's easy to work-around, but I wish I didn't need to consider this among the things constraining code's "portability to MicroPython".

symbioquine avatar Mar 06 '25 15:03 symbioquine

Note that the 2-argument next is implemented, guarded by MICROPY_PY_BUILTINS_NEXT2, see https://github.com/micropython/micropython/commit/42863830be2d19c7dcdf7ccf1fa66168b1bdc13a

stinos avatar Mar 07 '25 08:03 stinos

Okay, looking a bit harder, I actually found the relevant documentation: https://docs.micropython.org/en/latest/genrst/modules.html#second-argument-to-next-is-not-implemented

So I guess that documentation probably needs updating to clarify that it's now not available on some boards in the default builds and whether it is available is controlled by that compile time flag?

symbioquine avatar Mar 07 '25 13:03 symbioquine

Yeah I don't think there's a real policy now for 'conditionally different from CPython'. There are multiple other occurrences in these docs like that.

stinos avatar Mar 10 '25 08:03 stinos

Looking at this again after many years, I see that the difference in implementation between 1-arg next and 2-arg next is very small.

I made a comment long ago:

Implemented in C I think it'd be a large patch and therefore unlikely to be made part of default MicroPython builds.

I think my reasoning back then was because I was thinking that the implementation would need a NLR block to catch the stop iteration. But it doesn't anymore because we have mp_iternext_allow_raise and mp_iternext.

In my opinion the 2-argument form of next() should be enabled at a much earlier feature level, like MICROPY_CONFIG_ROM_LEVEL_BASIC_FEATURES. Or even just unconditionally replace the 1-arg form.

dpgeorge avatar Mar 10 '25 23:03 dpgeorge

See #16902.

dpgeorge avatar Mar 11 '25 02:03 dpgeorge

Now implemented.

dpgeorge avatar Mar 27 '25 00:03 dpgeorge