Hi Laurent, I'm still a dilettante, so take my comments with a grain of salt: 1. Target Python 3.6 only. (i.e. drop 3.5; look at 3.7 obv, but you want users now) (i.e. forget `yield from`, none will remember/get it next year) (if 2.7 or 3.3 must be supported, provide synch package) 2. Use futures (unless it's a stream) 3. Shield liberally 4. Provide context managers Naive user code might look like that: req = lib.request(...) await req.ready() # optional return await req.json()["something"] That's sane and pretty similar to http://aiohttp.readthedocs.io/en/stable/client.html thus your users will get it :) A more advanced use will be `[async] with lib.request(...) as r: await r.json()` (you probably want `async with` unless you can ensure synchronous timely termination) Personally I'd prefer `ready` and `json` without parentheses, but it seems I'm in a minority. Cheers, d. On 12 July 2017 at 00:26, Laurent Mazuel via Async-sig <async-sig@python.org> wrote:
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 _______________________________________________ Async-sig mailing list Async-sig@python.org https://mail.python.org/mailman/listinfo/async-sig Code of Conduct: https://www.python.org/psf/codeofconduct/