_helpers_py.py 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. """Various helper functions."""
  2. import sys
  3. from collections.abc import Mapping
  4. from functools import cached_property
  5. from typing import Any, Callable, Generic, Optional, Protocol, TypeVar, Union, overload
  6. __all__ = ("under_cached_property", "cached_property")
  7. if sys.version_info >= (3, 11):
  8. from typing import Self
  9. else:
  10. Self = Any
  11. _T = TypeVar("_T")
  12. # We use Mapping to make it possible to use TypedDict, but this isn't
  13. # technically type safe as we need to assign into the dict.
  14. _Cache = TypeVar("_Cache", bound=Mapping[str, Any])
  15. class _CacheImpl(Protocol[_Cache]):
  16. _cache: _Cache
  17. class under_cached_property(Generic[_T]):
  18. """Use as a class method decorator.
  19. It operates almost exactly like
  20. the Python `@property` decorator, but it puts the result of the
  21. method it decorates into the instance dict after the first call,
  22. effectively replacing the function it decorates with an instance
  23. variable. It is, in Python parlance, a data descriptor.
  24. """
  25. def __init__(self, wrapped: Callable[[Any], _T]) -> None:
  26. self.wrapped = wrapped
  27. self.__doc__ = wrapped.__doc__
  28. self.name = wrapped.__name__
  29. @overload
  30. def __get__(self, inst: None, owner: Optional[type[object]] = None) -> Self: ...
  31. @overload
  32. def __get__(self, inst: _CacheImpl[Any], owner: Optional[type[object]] = None) -> _T: ... # type: ignore[misc]
  33. def __get__(
  34. self, inst: Optional[_CacheImpl[Any]], owner: Optional[type[object]] = None
  35. ) -> Union[_T, Self]:
  36. if inst is None:
  37. return self
  38. try:
  39. return inst._cache[self.name] # type: ignore[no-any-return]
  40. except KeyError:
  41. val = self.wrapped(inst)
  42. inst._cache[self.name] = val
  43. return val
  44. def __set__(self, inst: _CacheImpl[Any], value: _T) -> None:
  45. raise AttributeError("cached property is read-only")