Uploaded image for project: 'Motor'
  1. Motor
  2. MOTOR-81

Cursor.close() from cursor-each callback doesn't stop iteration

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Major - P3
    • Resolution: Fixed
    • Affects Version/s: 0.5
    • Fix Version/s: 0.5
    • Component/s: asyncio
    • Labels:
      None

      Description

      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.

        Attachments

          Activity

            People

            Assignee:
            jesse A. Jesse Jiryu Davis
            Reporter:
            jesse A. Jesse Jiryu Davis
            Participants:
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved: