Module pyaww.utils.cache
TTL Cache functions for the API wrapper
Expand source code
"""TTL Cache functions for the API wrapper"""
# Standard library imports
import datetime
import asyncio
from typing import (
Optional,
TYPE_CHECKING,
Generic,
TypeVar,
Hashable,
Generator,
Union,
Any,
)
from collections.abc import MutableMapping
# Local application/library specific imports
if TYPE_CHECKING:
from pyaww import Console, SchedTask
KT = TypeVar("KT", bound=Hashable)
VT = TypeVar("VT")
def _check_if_expired(item: datetime.datetime) -> bool:
"""Check if a record has expired"""
return item <= datetime.datetime.now()
def _time(seconds: int) -> datetime.datetime:
"""Make a timedelta in the future"""
return datetime.datetime.now() + datetime.timedelta(seconds=seconds)
class TTLCache(MutableMapping[KT, VT], Generic[KT, VT]):
"""
TTL (time-to-live) cache for pyaww module. This class is utilised inside pyaww.utils.Cache. Records may expire
upon interacting with them (__getitem__ and __contains__.)
Ordinary format for the cache instance variable is the submodule initialized class id and the initialized class.
"""
def __init__(self, ttl_time: int = 30):
self.ttl = ttl_time
self.cache: dict[KT, tuple[VT, bool, datetime.datetime]] = {}
def __getitem__(self, item: KT) -> VT:
if item not in self:
raise KeyError
return self.cache[item][0]
def __contains__(self, item) -> bool:
try:
_, _, dt = self.cache[item]
if not _check_if_expired(dt):
return True
except KeyError:
return False
return False
def __setitem__(self, key: KT, value: VT) -> None:
self.cache[key] = value + (_time(self.ttl),) # type: ignore
def __len__(self) -> int:
return len(self.cache)
def __iter__(self) -> Generator:
yield from self.cache
def __delitem__(self, key: KT) -> None:
del self.cache[key]
def __str__(self) -> str:
return str(self.cache)
async def natural_values(self) -> list[VT]:
to_return: list[VT] = []
for x in self.cache.values():
if x[1]:
to_return.append(x[0])
return to_return
class Cache:
def __init__(self):
"""
Main caching class for the module.
Each implemented "type" (submodule) has its own get, set, delete, get_all and a record in the _submodule_dict
instance variable. Alongside each type, an instance variable (format: _type_cache) representing its cache will
be created with the value being initialized pyaww.TTLCache.
Anti-race-condition measures are taken into count here via asyncio.Lock().
"""
self.lock = asyncio.Lock()
self._console_cache: TTLCache[int, "Console"] = TTLCache()
self._sched_task_cache: TTLCache[int, "SchedTask"] = TTLCache()
self.use_cache = True
self.disable_cache_for_identifier = set()
self.disable_cache_for_module = set()
self._submodule_dict: dict[str, TTLCache] = {
"console": self._console_cache,
"sched_task": self._sched_task_cache,
} # PA support 3.10 smh
async def all(self, submodule: str) -> Optional[list[Any]]:
type_ = self._submodule_dict[submodule]
if submodule in self.disable_cache_for_module or not self.use_cache:
return None
return await type_.natural_values()
async def get(self, submodule: str, id_: int) -> Optional[Any]:
type_ = self._submodule_dict[submodule]
if (
submodule in self.disable_cache_for_module
or not self.use_cache
or id_ in self.disable_cache_for_identifier
):
return None
return type_.get(id_, None)
async def pop(self, submodule: str, id_: int) -> None:
type_ = self._submodule_dict[submodule]
async with self.lock:
type_.pop(id_)
async def set(
self,
submodule: str,
object_: Union[Any, list[Any]],
allow_all_usage: bool = False,
) -> None:
"""
Set something in the cache.
Args:
submodule (str): cached submodule from the pyaww dir
object_ (Union[Any, list[Any]): object to set in cache
allow_all_usage (bool): if set to true it will appear in the list that Cache.all returns.
Further explanation on allow_all_usage argument:
The reason for this argument is because the submodules' cache may be innacurate if the creator method
(e.g create_console) is called before the list method (e.g consoles) since, creator method will populate the
cache and list methods cache call statement will not evaluate to None and thus the request won't be called,
potentionally missing out some API results.
"""
type_ = self._submodule_dict[submodule]
if submodule in self.disable_cache_for_module or not self.use_cache:
return
async with self.lock:
if not isinstance(object_, list):
object_ = [object_]
for object_ in object_:
type_[object_.id] = (object_, allow_all_usage)
Classes
class Cache
-
Main caching class for the module.
Each implemented "type" (submodule) has its own get, set, delete, get_all and a record in the _submodule_dict instance variable. Alongside each type, an instance variable (format: _type_cache) representing its cache will be created with the value being initialized pyaww.TTLCache.
Anti-race-condition measures are taken into count here via asyncio.Lock().
Expand source code
class Cache: def __init__(self): """ Main caching class for the module. Each implemented "type" (submodule) has its own get, set, delete, get_all and a record in the _submodule_dict instance variable. Alongside each type, an instance variable (format: _type_cache) representing its cache will be created with the value being initialized pyaww.TTLCache. Anti-race-condition measures are taken into count here via asyncio.Lock(). """ self.lock = asyncio.Lock() self._console_cache: TTLCache[int, "Console"] = TTLCache() self._sched_task_cache: TTLCache[int, "SchedTask"] = TTLCache() self.use_cache = True self.disable_cache_for_identifier = set() self.disable_cache_for_module = set() self._submodule_dict: dict[str, TTLCache] = { "console": self._console_cache, "sched_task": self._sched_task_cache, } # PA support 3.10 smh async def all(self, submodule: str) -> Optional[list[Any]]: type_ = self._submodule_dict[submodule] if submodule in self.disable_cache_for_module or not self.use_cache: return None return await type_.natural_values() async def get(self, submodule: str, id_: int) -> Optional[Any]: type_ = self._submodule_dict[submodule] if ( submodule in self.disable_cache_for_module or not self.use_cache or id_ in self.disable_cache_for_identifier ): return None return type_.get(id_, None) async def pop(self, submodule: str, id_: int) -> None: type_ = self._submodule_dict[submodule] async with self.lock: type_.pop(id_) async def set( self, submodule: str, object_: Union[Any, list[Any]], allow_all_usage: bool = False, ) -> None: """ Set something in the cache. Args: submodule (str): cached submodule from the pyaww dir object_ (Union[Any, list[Any]): object to set in cache allow_all_usage (bool): if set to true it will appear in the list that Cache.all returns. Further explanation on allow_all_usage argument: The reason for this argument is because the submodules' cache may be innacurate if the creator method (e.g create_console) is called before the list method (e.g consoles) since, creator method will populate the cache and list methods cache call statement will not evaluate to None and thus the request won't be called, potentionally missing out some API results. """ type_ = self._submodule_dict[submodule] if submodule in self.disable_cache_for_module or not self.use_cache: return async with self.lock: if not isinstance(object_, list): object_ = [object_] for object_ in object_: type_[object_.id] = (object_, allow_all_usage)
Methods
async def all(self, submodule: str) ‑> Optional[list]
-
Expand source code
async def all(self, submodule: str) -> Optional[list[Any]]: type_ = self._submodule_dict[submodule] if submodule in self.disable_cache_for_module or not self.use_cache: return None return await type_.natural_values()
async def get(self, submodule: str, id_: int) ‑> Optional[Any]
-
Expand source code
async def get(self, submodule: str, id_: int) -> Optional[Any]: type_ = self._submodule_dict[submodule] if ( submodule in self.disable_cache_for_module or not self.use_cache or id_ in self.disable_cache_for_identifier ): return None return type_.get(id_, None)
async def pop(self, submodule: str, id_: int) ‑> None
-
Expand source code
async def pop(self, submodule: str, id_: int) -> None: type_ = self._submodule_dict[submodule] async with self.lock: type_.pop(id_)
async def set(self, submodule: str, object_: Union[Any, list[Any]], allow_all_usage: bool = False) ‑> None
-
Set something in the cache.
Args
submodule
:str
- cached submodule from the pyaww dir
object_
:Union[Any, list[Any]
- object to set in cache
allow_all_usage
:bool
- if set to true it will appear in the list that Cache.all returns.
Further explanation on allow_all_usage argument: The reason for this argument is because the submodules' cache may be innacurate if the creator method (e.g create_console) is called before the list method (e.g consoles) since, creator method will populate the cache and list methods cache call statement will not evaluate to None and thus the request won't be called, potentionally missing out some API results.
Expand source code
async def set( self, submodule: str, object_: Union[Any, list[Any]], allow_all_usage: bool = False, ) -> None: """ Set something in the cache. Args: submodule (str): cached submodule from the pyaww dir object_ (Union[Any, list[Any]): object to set in cache allow_all_usage (bool): if set to true it will appear in the list that Cache.all returns. Further explanation on allow_all_usage argument: The reason for this argument is because the submodules' cache may be innacurate if the creator method (e.g create_console) is called before the list method (e.g consoles) since, creator method will populate the cache and list methods cache call statement will not evaluate to None and thus the request won't be called, potentionally missing out some API results. """ type_ = self._submodule_dict[submodule] if submodule in self.disable_cache_for_module or not self.use_cache: return async with self.lock: if not isinstance(object_, list): object_ = [object_] for object_ in object_: type_[object_.id] = (object_, allow_all_usage)
class TTLCache (ttl_time: int = 30)
-
TTL (time-to-live) cache for pyaww module. This class is utilised inside pyaww.utils.Cache. Records may expire upon interacting with them (getitem and contains.)
Ordinary format for the cache instance variable is the submodule initialized class id and the initialized class.
Expand source code
class TTLCache(MutableMapping[KT, VT], Generic[KT, VT]): """ TTL (time-to-live) cache for pyaww module. This class is utilised inside pyaww.utils.Cache. Records may expire upon interacting with them (__getitem__ and __contains__.) Ordinary format for the cache instance variable is the submodule initialized class id and the initialized class. """ def __init__(self, ttl_time: int = 30): self.ttl = ttl_time self.cache: dict[KT, tuple[VT, bool, datetime.datetime]] = {} def __getitem__(self, item: KT) -> VT: if item not in self: raise KeyError return self.cache[item][0] def __contains__(self, item) -> bool: try: _, _, dt = self.cache[item] if not _check_if_expired(dt): return True except KeyError: return False return False def __setitem__(self, key: KT, value: VT) -> None: self.cache[key] = value + (_time(self.ttl),) # type: ignore def __len__(self) -> int: return len(self.cache) def __iter__(self) -> Generator: yield from self.cache def __delitem__(self, key: KT) -> None: del self.cache[key] def __str__(self) -> str: return str(self.cache) async def natural_values(self) -> list[VT]: to_return: list[VT] = [] for x in self.cache.values(): if x[1]: to_return.append(x[0]) return to_return
Ancestors
- collections.abc.MutableMapping
- collections.abc.Mapping
- collections.abc.Collection
- collections.abc.Sized
- collections.abc.Iterable
- collections.abc.Container
- typing.Generic
Methods
async def natural_values(self) ‑> list
-
Expand source code
async def natural_values(self) -> list[VT]: to_return: list[VT] = [] for x in self.cache.values(): if x[1]: to_return.append(x[0]) return to_return