]>
Commit | Line | Data |
---|---|---|
53e6db90 DC |
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 |