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