As demonstrated in TestAsyncIOCursor.test_each_close.
@asyncio_test def test_each_close(self): loop = self.loop collection = self.collection results = [] future = asyncio.Future(loop=self.loop) def callback(result, error): results.append(result) if len(results) == 50: # Prevent further calls. asyncio.Task(cursor.close(), loop=self.loop) loop.call_later(0.1, partial(future.set_result, None)) cursor = collection.find() cursor.each(callback) yield from future self.assertGreater(150, len(results))
"cursor.close" is supposed to set "cursor.closed" to True and stop iteration promptly - this should be the last call to "callback". "cursor.close" is a coroutine, but in Tornado this works anyway, since calling a coroutine without "yield" starts the coroutine and runs it to its first yield.
In asyncio, however, calling a coroutine without "yield from" does nothing. (Therefore, Nikolay Novik wrapped it in asyncio.Task when he ported test_each_close from Motor's tests to asyncio. But, since the Task runs asynchronously at an unpredictable future event loop iteration, it does not reliably cancel iteration with "each".) The "each" callback should "yield from cursor.close()" to run the coroutine, but the "each" callback isn't allowed to be a coroutine.
This doesn't really have to work, anyway: the "each" callback has a reliable method of canceling iteration, which is to return False. That's the documented method for canceling reliably. So probably close this wontfix, remove the tests, and check that the documentation is consistent on this point: the only right way to cancel iteration from within the callback is to return False.