Good point! I didn't see it. It will definitely happen. So I won't do that đ So, my best candidate right now is the "get_resource(nowait)" two methods approach.
Thank you! Laurent -----Original Message----- From: Chris Jerdonek [mailto:chris.jerdo...@gmail.com] Sent: Wednesday, July 12, 2017 13:27 To: Laurent Mazuel <lmaz...@microsoft.com> Cc: Cory Benfield <c...@lukasa.co.uk>; async-sig@python.org Subject: Re: [Async-sig] Optional async method and best practices 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://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail. > python.org%2Fmailman%2Flistinfo%2Fasync-sig&data=02%7C01%7Clmazuel%40m > icrosoft.com%7Cc1013246fdbf4f56115808d4c964635a%7C72f988bf86f141af91ab > 2d7cd011db47%7C1%7C0%7C636354880389311043&sdata=js9Q%2BwiV6IgMDgLDQs7e > shN8N0QCZypP7qX0IanKOlU%3D&reserved=0 > Code of Conduct: > https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.p > ython.org%2Fpsf%2Fcodeofconduct%2F&data=02%7C01%7Clmazuel%40microsoft. > com%7Cc1013246fdbf4f56115808d4c964635a%7C72f988bf86f141af91ab2d7cd011d > b47%7C1%7C0%7C636354880389311043&sdata=gmlfMEf3RQ6GXW4WwrPuPlTWWdxAqNa > muq8W%2B01LR6M%3D&reserved=0 _______________________________________________ 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/