[Async-sig] Optional async method and best practices

Laurent Mazuel lmazuel at microsoft.com
Tue Jul 11 18:26:58 EDT 2017


Hello,

I’m working currently with Brett Cannon to bring asyncio support to our SDK. We wanted to check with you one of the scenario, since we got a loooong discussion on it together 😊. And we want to do it using the best reasonable practice with your opinion.

We have an api that is clearly async and will gain a lot to be converted to asyncio. However, it's a two-step operation. Operation 1 asks for the creation of a resource and is not async, operation 2 is *optional* and wait for completion of this creation (with nightmare threads currently and I removed a lot of code moving to asyncio - happiness). There is perfectly legit scenarios where operation 2 is not needed and avoid it is better, but it has to be prepared at the same time of operation 1. Current code looks like this:

    sync_poller = client.create(**parameters)
    obj = sync_poller.resource() # Get the initial resource information, but the object is not actually created yet. 
    obj = sync_poller.result() # OPTIONAL. This is a blocking call with thread, if you want to wait for actual creation and get updated metadatas

My first prototype was to split and return a tuple (resource, coroutine):

    obj, optional_poller = client.create(**parameters)
    obj = await optional_poller # OPTIONAL

But I got a warning if I decide to do not use this poller, RuntimeWarning: coroutine 'foo' was never awaited 

I was surprised honestly that I can't do that, since I feel like I'm not leaking anything. I didn't run the operation, so there is no wasted resource at my knowledge. But I remember wasting time because of a forgotten "yield from", so I guess it's fair 😊. But I would be curious to understand what I did badly.

I found 2 solutions to avoid the warning, and I currently prefer solution 2:
1- Return a function to call, and not a coroutine. The "await" statement becomes:

   obj = await optional_poller()

2- Return my initial object with an async method. This allows me to write (something finally close to the current code):

    async_poller = client.create(**parameters)
    obj = async_poller.resource() # Get the initial resource information, but the object is not actually created yet. 
    obj = await async_poller.result() # OPTIONAL

My async_poller object being something like:

    class PollerOperation:
         async def result(self):
              ...async version of previous sync result()...

So the questions are:
- Does this seem a correct pattern?
- Is there a simple way to achieve something like this:

    obj = await async_poller

meaning, I can win the "result()" syntax and directly "await" on the object and get the result from magic function. I tried by subclassing some ABC coroutine/awaitable, but wasn't able to find a correct syntax. I'm not even sure this makes sense and respects the zen of Python 😊

If it helps, I'm willing to use 3.5 as minimal requirement to get async behavior.

Thank you!!

Laurent


More information about the Async-sig mailing list