Comments inlined:

On Thu, Sep 2, 2021 at 6:23 PM Guido van Rossum <guido@python.org> wrote:
First of all, we should ping Yury, who implemented `async for` about 6 years ago (see PEP 492), and Joshua Bronson, who implemented aiter() and anext() about 5 months ago (see https://bugs.python.org/issue31861). I've CC'ed them here.

Looks like PyAiter_Check was added along with the aiter/anext builtins. I agree it's unnecessary to check for __aiter__ in it, so I let's just fix it.

 

My own view:

A. iter() doesn't check that the thing returned implements __next__, because it's not needed -- iterators having an __iter__ methor is a convention, not a requirement.

Yeah.
 
You shouldn't implement __iter__ returning something that doesn't implement __iter__ itself, because then "for x in iter(a)" would fail even though "for x in a" works. But you get an error, and anyone who implements something like that (or uses it) deserves what they get. People know about this convention and the ABC enforces it, so in practice it will be very rare that someone gets bitten by this.

B. aiter() shouldn't need to check either, for exactly the same reason. I *suspect* (but do not know) that the extra check for the presence of __iter__ is simply an attempt by the implementer to enforce the convention. There is no *need* other than ensuring that "async for x in aiter(a)" works when "async for x in a" works.

I agree.

Note that PEP 525, which defines async generators, seems to imply that an __aiter__ returning self is always necessary, but I don't think it gives a reason.

PEP 525 implies that specifically for asynchronous generators, not iterators. That's due to the fact that synchronous generators return self from their __iter__.

I do notice there's some backwards compatibility issue related to __aiter__, alluded to in both PEP 492 (https://www.python.org/dev/peps/pep-0492/#api-design-and-implementation-revisions) and PEP 525 (https://www.python.org/dev/peps/pep-0525/#aiter-and-anext-builtins). So it's *possible* that it has to do with this (maybe really old code implementing the 3.5 version of __aiter__ would be caught out by the extra check) but I don't think it is. Hopefully Yury and/or Joshua remembers?

That wasn't related.

In the first iteration of PEP 492, __aiter__ was required to be a coroutine. Some time after shipping 3.5.0 I realized that that would complicate asynchronous generators for no reason (and I think there were also some bigger problems than just complicating them). So I updated the PEP to change __aiter__ return type from `Awaitable[AsyncIterator]` to `AsyncIterator`. ceval code was changed to call __aiter__ and see if the object that it returned had __anext__. If not, it tried to await on it.

Bottom line: let's fix PyAiter_Check to only look for __anext__. It's a new function so we can still fix it to reflect PyIter_Check and not worry about anything.

Yury