'Asynchronous property setter

Let us suppose we have a class with a property that can only be set 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?



Solution 1:[1]

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.

Solution 2:[2]

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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Ryan Anguiano