cache.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. from __future__ import unicode_literals
  2. from collections import deque
  3. from functools import wraps
  4. __all__ = (
  5. 'SimpleCache',
  6. 'FastDictCache',
  7. 'memoized',
  8. )
  9. class SimpleCache(object):
  10. """
  11. Very simple cache that discards the oldest item when the cache size is
  12. exceeded.
  13. :param maxsize: Maximum size of the cache. (Don't make it too big.)
  14. """
  15. def __init__(self, maxsize=8):
  16. assert isinstance(maxsize, int) and maxsize > 0
  17. self._data = {}
  18. self._keys = deque()
  19. self.maxsize = maxsize
  20. def get(self, key, getter_func):
  21. """
  22. Get object from the cache.
  23. If not found, call `getter_func` to resolve it, and put that on the top
  24. of the cache instead.
  25. """
  26. # Look in cache first.
  27. try:
  28. return self._data[key]
  29. except KeyError:
  30. # Not found? Get it.
  31. value = getter_func()
  32. self._data[key] = value
  33. self._keys.append(key)
  34. # Remove the oldest key when the size is exceeded.
  35. if len(self._data) > self.maxsize:
  36. key_to_remove = self._keys.popleft()
  37. if key_to_remove in self._data:
  38. del self._data[key_to_remove]
  39. return value
  40. def clear(self):
  41. " Clear cache. "
  42. self._data = {}
  43. self._keys = deque()
  44. class FastDictCache(dict):
  45. """
  46. Fast, lightweight cache which keeps at most `size` items.
  47. It will discard the oldest items in the cache first.
  48. The cache is a dictionary, which doesn't keep track of access counts.
  49. It is perfect to cache little immutable objects which are not expensive to
  50. create, but where a dictionary lookup is still much faster than an object
  51. instantiation.
  52. :param get_value: Callable that's called in case of a missing key.
  53. """
  54. # NOTE: This cache is used to cache `prompt_toolkit.layout.screen.Char` and
  55. # `prompt_toolkit.Document`. Make sure to keep this really lightweight.
  56. # Accessing the cache should stay faster than instantiating new
  57. # objects.
  58. # (Dictionary lookups are really fast.)
  59. # SimpleCache is still required for cases where the cache key is not
  60. # the same as the arguments given to the function that creates the
  61. # value.)
  62. def __init__(self, get_value=None, size=1000000):
  63. assert callable(get_value)
  64. assert isinstance(size, int) and size > 0
  65. self._keys = deque()
  66. self.get_value = get_value
  67. self.size = size
  68. def __missing__(self, key):
  69. # Remove the oldest key when the size is exceeded.
  70. if len(self) > self.size:
  71. key_to_remove = self._keys.popleft()
  72. if key_to_remove in self:
  73. del self[key_to_remove]
  74. result = self.get_value(*key)
  75. self[key] = result
  76. self._keys.append(key)
  77. return result
  78. def memoized(maxsize=1024):
  79. """
  80. Momoization decorator for immutable classes and pure functions.
  81. """
  82. cache = SimpleCache(maxsize=maxsize)
  83. def decorator(obj):
  84. @wraps(obj)
  85. def new_callable(*a, **kw):
  86. def create_new():
  87. return obj(*a, **kw)
  88. key = (a, tuple(kw.items()))
  89. return cache.get(key, create_new)
  90. return new_callable
  91. return decorator