ryananguiano / async_property

Python decorator for async properties.
MIT License
82 stars 8 forks source link

Async property can’t have a setter #19

Open AjnoEO opened 2 months ago

AjnoEO commented 2 months ago

Description

I created an async property. It needed a setter, so I made a synchronous setter for it. It raised an error saying async_property has no attribute 'setter'.

I am aware of the other async setter issue. It was, however, closed with the explanation that setters can’t be asynchronous. That is not my issue, I want a synchronous setter for an async property.

What I Did

The code

    @async_property
    async def channel(self) -> hikari.GuildChannel:
        """La steltabula kanalo"""
        if not self.__channel:
            self.__channel = await plugin.app.rest.fetch_channel(self.__channel_id)
        return self.__channel

    @channel.setter
    def channel(self, new_value: hikari.GuildChannel | int):
        if isinstance(new_value, hikari.GuildChannel):
            self.__channel = new_value
            self.__channel_id = new_value.id
        else:
            self.__channel_id = new_value
            self.__channel = None
        self.__update_in_database("steltabula-kanalo", self.__channel_id)

The traceback

  File "c:\Esperanto\Espiralo\steltabulo.py", line 76, in StarboardSettings
    @channel.setter
     ^^^^^^^^^^^^^^
AttributeError: 'AsyncPropertyDescriptor' object has no attribute 'setter'
ryananguiano commented 2 months ago

You should be using the async_cached_property instead

AjnoEO commented 2 months ago

Using async_cached_property kinda works, but now I have another issue

from async_property import async_cached_property
import asyncio

class TestClass:
    def __init__(self, value):
        self.__value = value.upper()

    @async_cached_property
    async def value(self):
        return self.__value

    @value.setter
    def value(self, new_value: str):
        self.__value = new_value.upper()

    def set_value(self, new_value: str):
        self.__value = new_value.upper()

async def main():
    test_obj = TestClass("first value")
    print(await test_obj.value)
    test_obj.value = "second value"
    print(await test_obj.value)
    test_obj.set_value("third value")
    print(await test_obj.value)

asyncio.run(main())

Editing a cached property is a hassle: I can’t modify the value in the setter, since it assumes I want to return the value I set it to, and a separate set method doesn’t affect the returned value at all. Is there a way to make sure each call of the setter clears the cache for that value?