On Wed, Jul 12, 2017 at 9:44 AM, Laurent Mazuel via Async-sig <async-sig@python.org> wrote: > @property > def future(self): > if self._future is None: > self._future = asyncio.Future() > asyncio.ensure_future(self._poll()) > return self._future > > result = await poller.future > > If I don't call the "future" attribute, I don't poll at all. My initial > "create" method returns the same object anytime, and I don't need a parameter > or another "nowait" method. Do you have any caveat for issues I don’t see in > this approach?
Hi Laurent, it seems like there is still an issue with this approach in that merely accessing / inspecting the property has the side effect of creating a coroutine object (calling self._poll()), and so can trigger the warning in innocent-looking code: loop = asyncio.get_event_loop() poller = PollerOperation() fut = poller.future # creates coroutine object if False: loop.run_until_complete(fut) loop.close() I'm not sure if others feel differently, but property access IMO shouldn't have possible side effects like this. If there are possible negative side effects, it should be a method call to indicate to the user that it is "doing" something that warrants more consideration. --Chris > > Thank you very much!!! > > Laurent > > PS: Cory, I just subscribed to the mailing list 😊 > > -----Original Message----- > From: Cory Benfield [mailto:c...@lukasa.co.uk] > Sent: Wednesday, July 12, 2017 01:18 > To: Laurent Mazuel <lmaz...@microsoft.com> > Cc: async-sig@python.org > Subject: Re: [Async-sig] Optional async method and best practices > > >> On 11 Jul 2017, at 23:26, Laurent Mazuel via Async-sig >> <async-sig@python.org> wrote: >> >> Hello, > > Hi Laurent! A future note: your message got stuck in moderation because you > aren’t subscribed to the mailing list. You may find it helpful to subscribe, > as your future messages will also get stuck unless you do! > >> 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. > > The assumption in asyncio, generally speaking, is that you do not create > coroutines you do not care about running. This is both for abstract > theoretical reasons (if you don’t care if the coroutine is run or not, why > not just optimise your code to never create the coroutine and save yourself > the CPU cycles?) and for more concrete practical concerns (coroutines may own > system resources and do cleanup in `finally` blocks, and if you don’t await a > coroutine then you’ll never reach the `finally` block and so will leak system > resources). > > Given that there’s no computational way to be sure that *not* running a > coroutine is safe (hello there halting problem), asyncio takes the pedantic > model and says that not running a coroutine is a condition that justifies a > warning. I think asyncio’s position here is correct, incidentally. > >> 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? > > Yes. This is the simple map to your old API, and is absolutely what I’d > recommend doing in the first instance if you want to use coroutines. > > >> - 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 😊 > > There are a few other patterns you could use. > > The first is to return a Future, and just always run the “polling” function > in the background to resolve that future. If the caller doesn’t care about > the result they can just ignore the Future, and if they do care they can > await on it. This has the downside of always requiring the I/O to poll, but > is otherwise pretty clean. > > Another option is to offer two functions, for example `def resource_nowait()` > and `async def resource`. The caller can decide which they want to call based > on whether they care about finding out the result. This is the clearest > approach that doesn’t trigger automatic extra work like the Future does, and > it lacks magic: it’s very declarative. This is a nice approach for keeping > things clean. > > Finally, you can create a magic awaitable object. PEP 492 defines several > ways to create an “awaitable”, but one approach is to use what it calls a > “Future-like object”: that is, one with a __await__ method that returns an > iterator. In this case, you’d do a very basic extension of the Future object > by triggering the work upon the call of __await__ before delegating to the > normal behaviour. This is an annoyingly precise thing to do, though > technically do-able. > > Cory > _______________________________________________ > 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/ _______________________________________________ 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/