forked from VimPlug/jedi
147 lines
4.5 KiB
Python
147 lines
4.5 KiB
Python
import time
|
|
import settings
|
|
|
|
# memoize caches will be deleted after every action
|
|
memoize_caches = []
|
|
|
|
time_caches = []
|
|
|
|
star_import_cache = {}
|
|
|
|
# for fast_parser, should not be deleted
|
|
parser_cache = {}
|
|
# should also not be deleted
|
|
module_cache = {}
|
|
|
|
|
|
def clear_caches(delete_all=False):
|
|
""" Jedi caches many things, that should be completed after each completion
|
|
finishes.
|
|
|
|
:param delete_all: Deletes also the cache that is normally not deleted,
|
|
like parser cache, which is important for faster parsing.
|
|
"""
|
|
global memoize_caches, time_caches
|
|
|
|
# memorize_caches must never be deleted, because the dicts will get lost in
|
|
# the wrappers.
|
|
for m in memoize_caches:
|
|
m.clear()
|
|
|
|
if delete_all:
|
|
time_caches = []
|
|
star_import_cache.clear()
|
|
parser_cache.clear()
|
|
module_cache.clear()
|
|
else:
|
|
# normally just kill the expired entries, not all
|
|
for tc in time_caches:
|
|
# check time_cache for expired entries
|
|
for key, (t, value) in list(tc.items()):
|
|
if t < time.time():
|
|
# delete expired entries
|
|
del tc[key]
|
|
|
|
|
|
def memoize_default(default=None, cache=memoize_caches):
|
|
""" This is a typical memoization decorator, BUT there is one difference:
|
|
To prevent recursion it sets defaults.
|
|
|
|
Preventing recursion is in this case the much bigger use than speed. I
|
|
don't think, that there is a big speed difference, but there are many cases
|
|
where recursion could happen (think about a = b; b = a).
|
|
"""
|
|
def func(function):
|
|
memo = {}
|
|
cache.append(memo)
|
|
|
|
def wrapper(*args, **kwargs):
|
|
key = (args, frozenset(kwargs.items()))
|
|
if key in memo:
|
|
return memo[key]
|
|
else:
|
|
memo[key] = default
|
|
rv = function(*args, **kwargs)
|
|
memo[key] = rv
|
|
return rv
|
|
return wrapper
|
|
return func
|
|
|
|
|
|
class CachedMetaClass(type):
|
|
""" This is basically almost the same than the decorator above, it just
|
|
caches class initializations. I haven't found any other way, so I do it
|
|
with meta classes.
|
|
"""
|
|
@memoize_default()
|
|
def __call__(self, *args, **kwargs):
|
|
return super(CachedMetaClass, self).__call__(*args, **kwargs)
|
|
|
|
|
|
def time_cache(time_add_setting):
|
|
""" This decorator works as follows: Call it with a setting and after that
|
|
use the function with a callable that returns the key.
|
|
But: This function is only called if the key is not available. After a
|
|
certain amount of time (`time_add_setting`) the cache is invalid.
|
|
"""
|
|
def _temp(key_func):
|
|
dct = {}
|
|
time_caches.append(dct)
|
|
def wrapper(optional_callable, *args, **kwargs):
|
|
key = key_func(*args, **kwargs)
|
|
value = None
|
|
if key in dct:
|
|
expiry, value = dct[key]
|
|
if expiry > time.time():
|
|
return value
|
|
value = optional_callable()
|
|
time_add = getattr(settings, time_add_setting)
|
|
if key is not None:
|
|
dct[key] = time.time() + time_add, value
|
|
return value
|
|
return wrapper
|
|
return _temp
|
|
|
|
|
|
@time_cache("get_in_function_call_validity")
|
|
def cache_get_in_function_call(stmt):
|
|
module_path = stmt.get_parent_until().path
|
|
return None if module_path is None else (module_path, stmt.start_pos)
|
|
|
|
|
|
def cache_star_import(func):
|
|
def wrapper(scope, *args, **kwargs):
|
|
try:
|
|
mods = star_import_cache[scope]
|
|
if mods[0] + settings.star_import_cache_validity > time.time():
|
|
return mods[1]
|
|
except KeyError:
|
|
pass
|
|
# cache is too old and therefore invalid or not available
|
|
invalidate_star_import_cache(scope)
|
|
mods = func(scope, *args, **kwargs)
|
|
star_import_cache[scope] = time.time(), mods
|
|
|
|
return mods
|
|
return wrapper
|
|
|
|
|
|
def invalidate_star_import_cache(module, only_main=False):
|
|
""" Important if some new modules are being reparsed """
|
|
try:
|
|
t, mods = star_import_cache[module]
|
|
|
|
del star_import_cache[module]
|
|
|
|
for m in mods:
|
|
invalidate_star_import_cache(m, only_main=True)
|
|
except KeyError:
|
|
pass
|
|
|
|
if not only_main:
|
|
# We need a list here because otherwise the list is being changed
|
|
# during the iteration in py3k: iteritems -> items.
|
|
for key, (t, mods) in list(star_import_cache.items()):
|
|
if module in mods:
|
|
invalidate_star_import_cache(key)
|