stubtest: relax async checking for abstract methods
Description
As has been pointed out in https://github.com/python/typeshed/pull/7475, the current typeshed definitions for various async methods in the stub for typing are problematic.
Until recently, these were all "synchronous" methods that returned Awaitable[X]. However, they were recently changed to be async def methods. This more closely match their definitions at runtime, but is problematic: the classes in typing.pyi represent abstract interfaces, and it's not true that an object has to return a coroutine from its __anext__ method in order to be considered an AyncIterator (it just has to return something that's awaitable).
Unfortunately, following https://github.com/python/mypy/pull/12212, stubtest will now emit an error if a method is async at runtime but not in the stub.
I think it makes sense to relax this check for abstract methods, so that's what this PR proposes doing.
Test Plan
Three new test cases added to teststubtest.py.
cc. @hauntsaninja, @graingert
According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉
According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉
According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉
According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉
According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉
According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉
I'm not sure I fully understand this change, but I'm not yet convinced it's better than an allowlist entry.
I see it as similar to the various synchronous abstract methods where we have a looser signature in the stub than the default implementation at runtime.
For example, collections.abc.Iterator.__iter__ and contextlib.AbstractContextManager.__enter__ both return self at runtime. But we wouldn't want them to be annotated with -> Self in the stubs, because these classes are abstract interfaces that represent formal definitions of what it means to be an "iterator" or a "context manager", and a class doesn't have to return self from its __iter__ method order for it to be considered an "iterator".
In the same way, AsyncIterator.__anext__ is a coroutine function at runtime -- that's the default implementation. But a class's __anext__ method doesn't have to be a coroutine function in order for it to be considered an "asynchronous iterator" (though it does need to return an awaitable). So it makes sense to have a looser signature for AsyncIterator.__anext__ than the default implementation, just like it does for collections.abc.Iterator.__iter__ and contextlib.AbstractContextManager.__enter__.
It seems to me that classes with abstract methods are generally going to represent abstract interfaces of some kind. As such, it might often be the case that the default implementation of an abstract method is a coroutine function, even if subclasses are not necessarily expected to implement the method as a coroutine function in order to be considered compatible with the interface. Hence, this PR :)
If you're still not convinced, feel free to close this, no hard feelings :)
According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉
According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉
According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉
According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅