You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

146 lines
4.2 KiB

4 years ago
  1. """
  2. This caching is very important for speed and memory optimizations. There's
  3. nothing really spectacular, just some decorators. The following cache types are
  4. available:
  5. - ``time_cache`` can be used to cache something for just a limited time span,
  6. which can be useful if there's user interaction and the user cannot react
  7. faster than a certain time.
  8. This module is one of the reasons why |jedi| is not thread-safe. As you can see
  9. there are global variables, which are holding the cache information. Some of
  10. these variables are being cleaned after every API usage.
  11. """
  12. import time
  13. from functools import wraps
  14. from jedi import settings
  15. from parso.cache import parser_cache
  16. _time_caches = {}
  17. def underscore_memoization(func):
  18. """
  19. Decorator for methods::
  20. class A(object):
  21. def x(self):
  22. if self._x:
  23. self._x = 10
  24. return self._x
  25. Becomes::
  26. class A(object):
  27. @underscore_memoization
  28. def x(self):
  29. return 10
  30. A now has an attribute ``_x`` written by this decorator.
  31. """
  32. name = '_' + func.__name__
  33. def wrapper(self):
  34. try:
  35. return getattr(self, name)
  36. except AttributeError:
  37. result = func(self)
  38. setattr(self, name, result)
  39. return result
  40. return wrapper
  41. def clear_time_caches(delete_all=False):
  42. """ Jedi caches many things, that should be completed after each completion
  43. finishes.
  44. :param delete_all: Deletes also the cache that is normally not deleted,
  45. like parser cache, which is important for faster parsing.
  46. """
  47. global _time_caches
  48. if delete_all:
  49. for cache in _time_caches.values():
  50. cache.clear()
  51. parser_cache.clear()
  52. else:
  53. # normally just kill the expired entries, not all
  54. for tc in _time_caches.values():
  55. # check time_cache for expired entries
  56. for key, (t, value) in list(tc.items()):
  57. if t < time.time():
  58. # delete expired entries
  59. del tc[key]
  60. def call_signature_time_cache(time_add_setting):
  61. """
  62. This decorator works as follows: Call it with a setting and after that
  63. use the function with a callable that returns the key.
  64. But: This function is only called if the key is not available. After a
  65. certain amount of time (`time_add_setting`) the cache is invalid.
  66. If the given key is None, the function will not be cached.
  67. """
  68. def _temp(key_func):
  69. dct = {}
  70. _time_caches[time_add_setting] = dct
  71. def wrapper(*args, **kwargs):
  72. generator = key_func(*args, **kwargs)
  73. key = next(generator)
  74. try:
  75. expiry, value = dct[key]
  76. if expiry > time.time():
  77. return value
  78. except KeyError:
  79. pass
  80. value = next(generator)
  81. time_add = getattr(settings, time_add_setting)
  82. if key is not None:
  83. dct[key] = time.time() + time_add, value
  84. return value
  85. return wrapper
  86. return _temp
  87. def time_cache(seconds):
  88. def decorator(func):
  89. cache = {}
  90. @wraps(func)
  91. def wrapper(*args, **kwargs):
  92. key = (args, frozenset(kwargs.items()))
  93. try:
  94. created, result = cache[key]
  95. if time.time() < created + seconds:
  96. return result
  97. except KeyError:
  98. pass
  99. result = func(*args, **kwargs)
  100. cache[key] = time.time(), result
  101. return result
  102. wrapper.clear_cache = lambda: cache.clear()
  103. return wrapper
  104. return decorator
  105. def memoize_method(method):
  106. """A normal memoize function."""
  107. @wraps(method)
  108. def wrapper(self, *args, **kwargs):
  109. cache_dict = self.__dict__.setdefault('_memoize_method_dct', {})
  110. dct = cache_dict.setdefault(method, {})
  111. key = (args, frozenset(kwargs.items()))
  112. try:
  113. return dct[key]
  114. except KeyError:
  115. result = method(self, *args, **kwargs)
  116. dct[key] = result
  117. return result
  118. return wrapper