Consider some library with an interface like this:
RemoteTask.start()
RemoteTask.cancel()
RemoteTask.get_id()
RemoteTask.get_result()
RemoteTask.is_done()
For example, concurrent.futures.Future
implements an API like this, but I don't want to assume the presence of a function like concurrent.futures.wait
.
In traditional Python code, you might need to poll for results:
def foo():
task = RemoteTask()
while not task.is_done():
time.sleep(2)
return task.get_result()
Is there some general recommended best-practice technique for wrapping this in an Awaitable
interface?
The desired usage would be:
async def foo():
task = RemoteTask()
return await run_remote_task()
I understand that the implementation details might differ across async libraries, so I am open to both general strategies for solving this problem, and specific solutions for Asyncio, Trio, AnyIO, or even Curio.
Assume that this library cannot be easily modified, and must be wrapped.
As the answer by Matthias says, if there is a way to block waiting for the remote task to complete (something like
task.wait_until_done()
) then you can turn that into an async wait function using the respective libraries' functions to run blocking code in a thread.Failing that, and assuming that none of those functions block, then polling will still work. If you don't want to handle cancellation then it looks almost identical to the synchronous version and would be very similar across the different frameworks. It looks like this:
The most basic way to handle cancellation is to cancel the remote task if the async task is cancelled, but then don't wait for remote cancellation to complete. For that, things are still fairly simple:
Again, that would look almost identical on the other frameworks too. Just replace
trio.sleep()
withasyncio.sleep()
oranyio.sleep()
.If you want to wait for the remote cancellation to finish, things are slightly more fiddly. This is what it would look like in Trio (anyio is the same but I'm not sure about asyncio):