On Wed, Jul 12, 2017 at 9:44 AM, Laurent Mazuel via Async-sig
<[email protected]> 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:[email protected]]
> Sent: Wednesday, July 12, 2017 01:18
> To: Laurent Mazuel <[email protected]>
> Cc: [email protected]
> Subject: Re: [Async-sig] Optional async method and best practices
>
>
>> On 11 Jul 2017, at 23:26, Laurent Mazuel via Async-sig
>> <[email protected]> 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
> [email protected]
> https://mail.python.org/mailman/listinfo/async-sig
> Code of Conduct: https://www.python.org/psf/codeofconduct/
_______________________________________________
Async-sig mailing list
[email protected]
https://mail.python.org/mailman/listinfo/async-sig
Code of Conduct: https://www.python.org/psf/codeofconduct/