compat.py 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. # SPDX-License-Identifier: AGPL-3.0-or-later
  2. # lint: pylint
  3. # pyright: basic
  4. """Module for backward compatibility.
  5. """
  6. # pylint: disable=C,R
  7. try:
  8. from functools import cached_property # pylint: disable=unused-import
  9. except ImportError:
  10. # cache_property has been added in py3.8 [1]
  11. #
  12. # To support cache_property in py3.7 the implementation from 3.8 has been
  13. # copied here. This code can be cleanup with EOL of py3.7.
  14. #
  15. # [1] https://docs.python.org/3/library/functools.html#functools.cached_property
  16. from threading import RLock
  17. _NOT_FOUND = object()
  18. class cached_property:
  19. def __init__(self, func):
  20. self.func = func
  21. self.attrname = None
  22. self.__doc__ = func.__doc__
  23. self.lock = RLock()
  24. def __set_name__(self, owner, name):
  25. if self.attrname is None:
  26. self.attrname = name
  27. elif name != self.attrname:
  28. raise TypeError(
  29. "Cannot assign the same cached_property to two different names "
  30. f"({self.attrname!r} and {name!r})."
  31. )
  32. def __get__(self, instance, owner=None):
  33. if instance is None:
  34. return self
  35. if self.attrname is None:
  36. raise TypeError("Cannot use cached_property instance without calling __set_name__ on it.")
  37. try:
  38. cache = instance.__dict__
  39. except AttributeError: # not all objects have __dict__ (e.g. class defines slots)
  40. msg = (
  41. f"No '__dict__' attribute on {type(instance).__name__!r} "
  42. f"instance to cache {self.attrname!r} property."
  43. )
  44. raise TypeError(msg) from None
  45. val = cache.get(self.attrname, _NOT_FOUND)
  46. if val is _NOT_FOUND:
  47. with self.lock:
  48. # check if another thread filled cache while we awaited lock
  49. val = cache.get(self.attrname, _NOT_FOUND)
  50. if val is _NOT_FOUND:
  51. val = self.func(instance)
  52. try:
  53. cache[self.attrname] = val
  54. except TypeError:
  55. msg = (
  56. f"The '__dict__' attribute on {type(instance).__name__!r} instance "
  57. f"does not support item assignment for caching {self.attrname!r} property."
  58. )
  59. raise TypeError(msg) from None
  60. return val