dpetican dpetican - 5 months ago 7
Python Question

Python using futures with loop_forever

Just started experimenting with asynch which looks really cool. I'm trying to use futures with an asynch coroutine that runs forever but I get this error:

Task exception was never retrieved
future: <Task finished coro=<slow_operation() done, defined at ./asynchio-test3.py:5> exception=InvalidStateError("FINISHED: <Future finished result='This is the future!'>",)>


This is my code which runs as expected if I remove the 3 lines related to futures:

import asyncio

@asyncio.coroutine
def slow_operation():
yield from asyncio.sleep(1)
print ("This is the task!")
future.set_result('This is the future!')
asyncio.async(slow_operation())

def got_result(future):
print(future.result())

loop = asyncio.get_event_loop()
future = asyncio.Future()
future.add_done_callback(got_result)
asyncio.async(slow_operation())
try:
loop.run_forever()
finally:
loop.close()

Answer

slow_operator is called indefinitely, calling set_result for the same future object multiple times; which is not possbile.

>>> import asyncio
>>> future = asyncio.Future()
>>> future.set_result('result')
>>> future.set_result('result')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python35\lib\asyncio\futures.py", line 329, in set_result
    raise InvalidStateError('{}: {!r}'.format(self._state, self))
asyncio.futures.InvalidStateError: FINISHED: <Future finished result='result'>

Create new future for each slow_operator call. For example:

@asyncio.coroutine
def slow_operation(future):
    yield from asyncio.sleep(1)
    print ("This is the task!")
    future.set_result('This is the future!')
    asyncio.async(slow_operation(new_future()))

def got_result(future):
    print(future.result())

def new_future():
    future = asyncio.Future()
    future.add_done_callback(got_result)
    return future

loop = asyncio.get_event_loop()
asyncio.async(slow_operation(new_future()))
try:
    loop.run_forever()
finally:
    loop.close()

BTW, you can use new syntax (async, await) if you're using Python 3.5+:

async def slow_operation(future):
    await asyncio.sleep(1)
    print ("This is the task!")
    future.set_result('This is the future!')
    asyncio.ensure_future(slow_operation(new_future()))
Comments