Skip to content

Commit

Permalink
ADCM-6143: Change cached_property to async_cached_property (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
DanBalalan authored and a-alferov committed Jan 24, 2025
1 parent 04a9e05 commit 0d69bf0
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 7 deletions.
4 changes: 3 additions & 1 deletion adcm_aio_client/core/objects/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from functools import cached_property
from typing import Any, Self

from asyncstdlib.functools import CachedProperty

from adcm_aio_client.core.requesters import Requester
from adcm_aio_client.core.types import AwareOfOwnPath, Endpoint, WithRequester

Expand All @@ -18,7 +20,7 @@ def __init_subclass__(cls: type[Self]) -> None:
for name in dir(cls):
# None is for declared, but unset values
attr = getattr(cls, name, None)
if isinstance(attr, cached_property): # TODO: asyncstdlib.functools.CachedProperty
if isinstance(attr, (cached_property, CachedProperty)):
cls._delete_on_refresh.append(name)

def __init__(self: Self, requester: Requester, data: dict[str, Any]) -> None:
Expand Down
12 changes: 7 additions & 5 deletions adcm_aio_client/core/objects/cm.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from typing import Iterable, Self
import asyncio

from asyncstdlib.functools import cached_property as async_cached_property

from adcm_aio_client.core.errors import NotFoundError, OperationError, ResponseError
from adcm_aio_client.core.objects._accessors import (
PaginatedAccessor,
Expand Down Expand Up @@ -29,7 +31,7 @@ class ADCM(InteractiveObject, WithActions, WithConfig):
def id(self: Self) -> int:
return 1

@cached_property
@async_cached_property
async def version(self: Self) -> str:
# TODO: override root_path for being without /api/v2
response = await self._requester.get("versions")
Expand Down Expand Up @@ -68,7 +70,7 @@ def description(self: Self) -> str:
# todo think how such properties will be invalidated when data is updated
# during `refresh()` / `reread()` calls.
# See cache invalidation or alternatives in documentation for `cached_property`
@cached_property # TODO: replace with asyncstdlib.functools.cached_property
@async_cached_property
async def bundle(self: Self) -> Bundle:
prototype_id = self._data["prototype"]["id"]
response = await self._requester.get("prototypes", prototype_id)
Expand Down Expand Up @@ -162,7 +164,7 @@ def name(self: Self) -> str:
def display_name(self: Self) -> str:
return self._data["displayName"]

@cached_property # TODO: replace with asyncstdlib.functools.cached_property
@async_cached_property
async def constraint(self: Self) -> list[int | str]:
response = (await self._requester.get(*self.cluster.get_own_path(), "mapping", "components")).as_list()
for component in response:
Expand Down Expand Up @@ -240,13 +242,13 @@ async def get_status(self: Self) -> ADCMEntityStatus:
response = await self._requester.get(*self.get_own_path())
return ADCMEntityStatus(response.as_dict()["status"])

@cached_property # TODO: replace with asyncstdlib.functools.cached_property
@async_cached_property
async def cluster(self: Self) -> Cluster | None:
if not self._data["cluster"]:
return None
return await Cluster.with_id(requester=self._requester, object_id=self._data["cluster"]["id"])

@cached_property # TODO: replace with asyncstdlib.functools.cached_property
@async_cached_property
async def hostprovider(self: Self) -> HostProvider:
return await HostProvider.with_id(requester=self._requester, object_id=self._data["hostprovider"]["id"])

Expand Down
18 changes: 17 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ readme = "README.md"
[tool.poetry.dependencies]
python = "^3.12"
httpx = "^0.27.2"
asyncstdlib = "^3.13.0"

[tool.poetry.group.dev]
optional = true
Expand Down
34 changes: 34 additions & 0 deletions tests/integration/test_misc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from typing import Self

from asyncstdlib.functools import cached_property as async_cached_property
import pytest

pytestmark = [pytest.mark.asyncio]


class Dummy:
def __init__(self: Self) -> None:
self.counter = 0

@async_cached_property
async def func(self: Self) -> int:
self.counter += 1

return self.counter


async def test_async_cached_property() -> None:
obj = Dummy()
assert "func" not in obj.__dict__, "`func` key should not be cached yet"

res = await obj.func
assert res == 1
assert "func" in obj.__dict__, "`func` key should be cached"

res = await obj.func
assert res == 1, "Cached value must be used"

delattr(obj, "func")
res = await obj.func
assert res == 2, "Expected to execute func() again, increasing the counter"
assert "func" in obj.__dict__

0 comments on commit 0d69bf0

Please sign in to comment.