Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronous property setter

Let us suppose we have a class with a property that can only be setted asynchronously. Is there a way to make this work without explicitly calling a setter ?

MNWE:

import asyncio

loop = asyncio.get_event_loop()

class AsyncTest:
    def __init__(self, attrib):
        self._attrib = attrib

    @property
    def attrib(self):
        return self._attrib

    @attrib.setter
    async def set_attrib(self, attrib):
        await asyncio.sleep(1.0)
        self._attrib = attrib


async def main():
    t = AsyncTest(1)
    print(t.attrib)
    await t.attrib = 3
    print(t.attrib)

asyncio.ensure_future(main())
loop.run_forever()

This fails with

  File "asyncprop.py", line 22
    await t.attrib = 3
       ^
SyntaxError: can't assign to await expression

which is not surprising, given that the grammar for await is

await ::=  ["await"] primary

So it seems we are bound to forget this nice @property and resign ourselves to using getters and setters for asynchronous operations. Did I miss something?

like image 850
Evpok Avatar asked Apr 16 '16 15:04

Evpok


2 Answers

You can use the async-property package: https://pypi.org/project/async-property/

Example:

from async_property import async_property

class Foo:
    @async_property
    async def remote_value(self):
        return await get_remote_value()

f = Foo()
await f.remote_value
like image 138
Ryan Anguiano Avatar answered Sep 27 '22 20:09

Ryan Anguiano


You can't nest a statement inside another statement; assignment is a statement, and so is await. You could use setattr() to set attributes in an expression:

await setattr(t, 'attrib', 3)

However, a property wraps the setter in a way that does not support async methods (they are not awaitable), so you are still better off with an explicit setter method.

like image 35
Martijn Pieters Avatar answered Sep 27 '22 19:09

Martijn Pieters