1
0
forked from VimPlug/jedi

caches have their own module now.

This commit is contained in:
David Halter
2012-12-12 21:28:19 +01:00
parent 169b35b5a9
commit e8ddc35a1b
9 changed files with 109 additions and 97 deletions

View File

@@ -442,7 +442,7 @@ class Script(object):
return match.groups() return match.groups()
def __del__(self): def __del__(self):
evaluate.clear_caches() api_classes._clear_caches()
def set_debug_function(func_cb=debug.print_to_stdout, warnings=True, def set_debug_function(func_cb=debug.print_to_stdout, warnings=True,

View File

@@ -3,6 +3,9 @@
import re import re
import os import os
import cache
import dynamic
import helpers
import settings import settings
import evaluate import evaluate
import imports import imports
@@ -10,6 +13,20 @@ import parsing
import keywords import keywords
def _clear_caches():
"""
Clears all caches of this and related modules. The only cache that will not
be deleted is the module cache.
"""
cache.clear_caches()
dynamic.search_param_cache.clear()
helpers.ExecutionRecursionDecorator.reset()
evaluate.follow_statement.reset()
imports.imports_processed = 0
class BaseDefinition(object): class BaseDefinition(object):
_mapping = {'posixpath': 'os.path', _mapping = {'posixpath': 'os.path',
'riscospath': 'os.path', 'riscospath': 'os.path',
@@ -193,7 +210,7 @@ class Completion(BaseDefinition):
self._followed_definitions = \ self._followed_definitions = \
[BaseDefinition(d, d.start_pos) for d in defs] [BaseDefinition(d, d.start_pos) for d in defs]
evaluate.clear_caches() _clear_caches()
return self._followed_definitions return self._followed_definitions

53
jedi/cache.py Normal file
View File

@@ -0,0 +1,53 @@
memoize_caches = []
faked_scopes = []
def clear_caches():
""" Jedi caches many things, that should be completed after each completion
finishes.
"""
global memoize_caches, faked_scopes
# memorize_caches must never be deleted, because the dicts will get lost in
# the wrappers.
for m in memoize_caches:
m.clear()
faked_scopes = [] # TODO really needed anymore? weakrefs are gone...
def memoize_default(default=None):
""" 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 = {}
memoize_caches.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)

View File

@@ -5,7 +5,7 @@ import re
import evaluate import evaluate
#@evaluate.memoize_default() # TODO add #@cache.memoize_default() # TODO add
def follow_param(param): def follow_param(param):
func = param.parent_function func = param.parent_function
#print func, param, param.parent_function #print func, param, param.parent_function

View File

@@ -9,6 +9,7 @@ from __future__ import with_statement
import os import os
import cache
import parsing import parsing
import modules import modules
import evaluate import evaluate
@@ -93,7 +94,7 @@ class ParamListener(object):
self.param_possibilities.append(params) self.param_possibilities.append(params)
@evaluate.memoize_default([]) @cache.memoize_default([])
def search_params(param): def search_params(param):
""" """
This is a dynamic search for params. If you try to complete a type: This is a dynamic search for params. If you try to complete a type:
@@ -223,7 +224,7 @@ def dec(func):
#@dec #@dec
@evaluate.memoize_default([]) @cache.memoize_default([])
def _check_array_additions(compare_array, module, is_list): def _check_array_additions(compare_array, module, is_list):
""" """
Checks if a `parsing.Array` has "add" statements: Checks if a `parsing.Array` has "add" statements:

View File

@@ -18,6 +18,7 @@ import sys
import itertools import itertools
import copy import copy
import cache
import parsing import parsing
import debug import debug
import builtin import builtin
@@ -26,9 +27,6 @@ import helpers
import dynamic import dynamic
import docstrings import docstrings
memoize_caches = []
faked_scopes = []
class DecoratorNotFound(LookupError): class DecoratorNotFound(LookupError):
""" """
@@ -63,66 +61,6 @@ class MultiLevelAttributeError(Exception):
return 'Original:\n\n' + ''.join(tb) return 'Original:\n\n' + ''.join(tb)
def clear_caches():
"""
Clears all caches of this and related modules. Jedi caches many things,
that should be completed after each completion finishes. The only things
that stays is the module cache (which is not deleted here).
"""
global memoize_caches, faked_scopes
for m in memoize_caches:
m.clear()
dynamic.search_param_cache.clear()
helpers.ExecutionRecursionDecorator.reset()
# memorize_caches must never be deleted, because the dicts will get lost in
# the wrappers.
faked_scopes = []
follow_statement.reset()
imports.imports_processed = 0
def memoize_default(default=None):
"""
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 = {}
memoize_caches.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)
class Executable(parsing.Base): class Executable(parsing.Base):
""" An instance is also an executable - because __init__ is called """ """ An instance is also an executable - because __init__ is called """
def __init__(self, base, var_args=None): def __init__(self, base, var_args=None):
@@ -140,7 +78,7 @@ class Executable(parsing.Base):
return self.base.parent return self.base.parent
class Instance(use_metaclass(CachedMetaClass, Executable)): class Instance(use_metaclass(cache.CachedMetaClass, Executable)):
""" This class is used to evaluate instances. """ """ This class is used to evaluate instances. """
def __init__(self, base, var_args=None): def __init__(self, base, var_args=None):
super(Instance, self).__init__(base, var_args) super(Instance, self).__init__(base, var_args)
@@ -159,7 +97,7 @@ class Instance(use_metaclass(CachedMetaClass, Executable)):
# (No var_args) used. # (No var_args) used.
self.is_generated = False self.is_generated = False
@memoize_default() @cache.memoize_default()
def get_init_execution(self, func): def get_init_execution(self, func):
func = InstanceElement(self, func, True) func = InstanceElement(self, func, True)
return Execution(func, self.var_args) return Execution(func, self.var_args)
@@ -227,7 +165,7 @@ class Instance(use_metaclass(CachedMetaClass, Executable)):
args = helpers.generate_param_array(v) args = helpers.generate_param_array(v)
return self.execute_subscope_by_name('__get__', args) return self.execute_subscope_by_name('__get__', args)
@memoize_default([]) @cache.memoize_default([])
def get_defined_names(self): def get_defined_names(self):
""" """
Get the instance vars of a class. This includes the vars of all Get the instance vars of a class. This includes the vars of all
@@ -273,7 +211,7 @@ class Instance(use_metaclass(CachedMetaClass, Executable)):
(type(self).__name__, self.base, len(self.var_args or [])) (type(self).__name__, self.base, len(self.var_args or []))
class InstanceElement(use_metaclass(CachedMetaClass)): class InstanceElement(use_metaclass(cache.CachedMetaClass)):
""" """
InstanceElement is a wrapper for any object, that is used as an instance InstanceElement is a wrapper for any object, that is used as an instance
variable (e.g. self.variable or class methods). variable (e.g. self.variable or class methods).
@@ -288,7 +226,7 @@ class InstanceElement(use_metaclass(CachedMetaClass)):
self.is_class_var = is_class_var self.is_class_var = is_class_var
@property @property
@memoize_default() @cache.memoize_default()
def parent(self): def parent(self):
par = self.var.parent par = self.var.parent
if isinstance(par, Class) and par == self.instance.base \ if isinstance(par, Class) and par == self.instance.base \
@@ -317,8 +255,8 @@ class InstanceElement(use_metaclass(CachedMetaClass)):
par = InstanceElement(self.instance, origin.parent_stmt, par = InstanceElement(self.instance, origin.parent_stmt,
self.is_class_var) self.is_class_var)
new.parent_stmt = par new.parent_stmt = par
faked_scopes.append(par) cache.faked_scopes.append(par)
faked_scopes.append(new) cache.faked_scopes.append(new)
return new return new
def __getattr__(self, name): def __getattr__(self, name):
@@ -331,7 +269,7 @@ class InstanceElement(use_metaclass(CachedMetaClass)):
return "<%s of %s>" % (type(self).__name__, self.var) return "<%s of %s>" % (type(self).__name__, self.var)
class Class(use_metaclass(CachedMetaClass, parsing.Base)): class Class(use_metaclass(cache.CachedMetaClass, parsing.Base)):
""" """
This class is not only important to extend `parsing.Class`, it is also a This class is not only important to extend `parsing.Class`, it is also a
important for descriptors (if the descriptor methods are evaluated or not). important for descriptors (if the descriptor methods are evaluated or not).
@@ -339,7 +277,7 @@ class Class(use_metaclass(CachedMetaClass, parsing.Base)):
def __init__(self, base): def __init__(self, base):
self.base = base self.base = base
@memoize_default(default=[]) @cache.memoize_default(default=[])
def get_super_classes(self): def get_super_classes(self):
supers = [] supers = []
# TODO care for mro stuff (multiple super classes). # TODO care for mro stuff (multiple super classes).
@@ -355,7 +293,7 @@ class Class(use_metaclass(CachedMetaClass, parsing.Base)):
supers += get_scopes_for_name(builtin.Builtin.scope, 'object') supers += get_scopes_for_name(builtin.Builtin.scope, 'object')
return supers return supers
@memoize_default(default=[]) @cache.memoize_default(default=[])
def get_defined_names(self): def get_defined_names(self):
def in_iterable(name, iterable): def in_iterable(name, iterable):
""" checks if the name is in the variable 'iterable'. """ """ checks if the name is in the variable 'iterable'. """
@@ -397,7 +335,7 @@ class Class(use_metaclass(CachedMetaClass, parsing.Base)):
return "<e%s of %s>" % (type(self).__name__, self.base) return "<e%s of %s>" % (type(self).__name__, self.base)
class Function(use_metaclass(CachedMetaClass, parsing.Base)): class Function(use_metaclass(cache.CachedMetaClass, parsing.Base)):
""" """
Needed because of decorators. Decorators are evaluated here. Needed because of decorators. Decorators are evaluated here.
""" """
@@ -408,7 +346,7 @@ class Function(use_metaclass(CachedMetaClass, parsing.Base)):
self.is_decorated = is_decorated self.is_decorated = is_decorated
@property @property
@memoize_default() @cache.memoize_default()
def _decorated_func(self): def _decorated_func(self):
""" """
Returns the function, that is to be executed in the end. Returns the function, that is to be executed in the end.
@@ -432,7 +370,7 @@ class Function(use_metaclass(CachedMetaClass, parsing.Base)):
# Create param array. # Create param array.
old_func = Function(f, is_decorated=True) old_func = Function(f, is_decorated=True)
params = helpers.generate_param_array([old_func], old_func) params = helpers.generate_param_array([old_func], old_func)
faked_scopes.append(old_func) cache.faked_scopes.append(old_func)
wrappers = Execution(decorator, params).get_return_types() wrappers = Execution(decorator, params).get_return_types()
if not len(wrappers): if not len(wrappers):
@@ -481,7 +419,7 @@ class Execution(Executable):
multiple calls to functions and recursion has to be avoided. But this is multiple calls to functions and recursion has to be avoided. But this is
responsibility of the decorators. responsibility of the decorators.
""" """
@memoize_default(default=[]) @cache.memoize_default(default=[])
@helpers.ExecutionRecursionDecorator @helpers.ExecutionRecursionDecorator
def get_return_types(self, evaluate_generator=False): def get_return_types(self, evaluate_generator=False):
""" Get the return types of a function. """ """ Get the return types of a function. """
@@ -555,7 +493,7 @@ class Execution(Executable):
stmts += follow_statement(r) stmts += follow_statement(r)
return stmts return stmts
@memoize_default(default=[]) @cache.memoize_default(default=[])
def get_params(self): def get_params(self):
""" """
This returns the params for an Execution/Instance and is injected as a This returns the params for an Execution/Instance and is injected as a
@@ -581,7 +519,7 @@ class Execution(Executable):
new_param.is_generated = True new_param.is_generated = True
name = copy.copy(param.get_name()) name = copy.copy(param.get_name())
name.parent = new_param name.parent = new_param
faked_scopes.append(new_param) cache.faked_scopes.append(new_param)
return name return name
result = [] result = []
@@ -737,7 +675,7 @@ class Execution(Executable):
if isinstance(copied, parsing.Function): if isinstance(copied, parsing.Function):
copied = Function(copied) copied = Function(copied)
objects.append(copied) objects.append(copied)
faked_scopes.append(copied) cache.faked_scopes.append(copied)
return objects return objects
def __getattr__(self, name): def __getattr__(self, name):
@@ -745,7 +683,7 @@ class Execution(Executable):
raise AttributeError('Tried to access %s: %s. Why?' % (name, self)) raise AttributeError('Tried to access %s: %s. Why?' % (name, self))
return getattr(self.base, name) return getattr(self.base, name)
@memoize_default() @cache.memoize_default()
def _scope_copy(self, scope): def _scope_copy(self, scope):
try: try:
""" Copies a scope (e.g. if) in an execution """ """ Copies a scope (e.g. if) in an execution """
@@ -758,28 +696,28 @@ class Execution(Executable):
else: else:
copied = helpers.fast_parent_copy(scope) copied = helpers.fast_parent_copy(scope)
copied.parent = self._scope_copy(copied.parent) copied.parent = self._scope_copy(copied.parent)
faked_scopes.append(copied) cache.faked_scopes.append(copied)
return copied return copied
except AttributeError: except AttributeError:
raise MultiLevelAttributeError(sys.exc_info()) raise MultiLevelAttributeError(sys.exc_info())
@property @property
@memoize_default() @cache.memoize_default()
def returns(self): def returns(self):
return self.copy_properties('returns') return self.copy_properties('returns')
@property @property
@memoize_default() @cache.memoize_default()
def asserts(self): def asserts(self):
return self.copy_properties('asserts') return self.copy_properties('asserts')
@property @property
@memoize_default() @cache.memoize_default()
def statements(self): def statements(self):
return self.copy_properties('statements') return self.copy_properties('statements')
@property @property
@memoize_default() @cache.memoize_default()
def subscopes(self): def subscopes(self):
return self.copy_properties('subscopes') return self.copy_properties('subscopes')
@@ -791,7 +729,7 @@ class Execution(Executable):
(type(self).__name__, self.base) (type(self).__name__, self.base)
class Generator(use_metaclass(CachedMetaClass, parsing.Base)): class Generator(use_metaclass(cache.CachedMetaClass, parsing.Base)):
""" Cares for `yield` statements. """ """ Cares for `yield` statements. """
def __init__(self, func, var_args): def __init__(self, func, var_args):
super(Generator, self).__init__() super(Generator, self).__init__()
@@ -830,7 +768,7 @@ class Generator(use_metaclass(CachedMetaClass, parsing.Base)):
return "<%s of %s>" % (type(self).__name__, self.func) return "<%s of %s>" % (type(self).__name__, self.func)
class Array(use_metaclass(CachedMetaClass, parsing.Base)): class Array(use_metaclass(cache.CachedMetaClass, parsing.Base)):
""" """
Used as a mirror to parsing.Array, if needed. It defines some getter Used as a mirror to parsing.Array, if needed. It defines some getter
methods which are important in this module. methods which are important in this module.
@@ -1380,7 +1318,7 @@ def assign_tuples(tup, results, seek_name):
@helpers.RecursionDecorator @helpers.RecursionDecorator
@memoize_default(default=[]) @cache.memoize_default(default=[])
def follow_statement(stmt, seek_name=None): def follow_statement(stmt, seek_name=None):
""" """
The starting point of the completion. A statement always owns a call list, The starting point of the completion. A statement always owns a call list,

View File

@@ -1,6 +1,7 @@
import copy import copy
import contextlib import contextlib
import cache
import parsing import parsing
import evaluate import evaluate
import debug import debug
@@ -195,7 +196,7 @@ def generate_param_array(args_tuple, parent_stmt=None):
values.append([arg]) values.append([arg])
pos = None pos = None
arr = parsing.Array(pos, parsing.Array.TUPLE, parent_stmt, values=values) arr = parsing.Array(pos, parsing.Array.TUPLE, parent_stmt, values=values)
evaluate.faked_scopes.append(arr) cache.faked_scopes.append(arr)
return arr return arr

View File

@@ -6,6 +6,7 @@ import imp
import sys import sys
import time import time
import cache
import builtin import builtin
import modules import modules
import debug import debug
@@ -91,7 +92,7 @@ class ImportPath(parsing.Base):
n = parsing.Name(i.namespace.names[1:], zero, zero, self.import_stmt) n = parsing.Name(i.namespace.names[1:], zero, zero, self.import_stmt)
new = parsing.Import(zero, zero, n) new = parsing.Import(zero, zero, n)
new.parent = parent new.parent = parent
evaluate.faked_scopes.append(new) cache.faked_scopes.append(new)
debug.dbg('Generated a nested import: %s' % new) debug.dbg('Generated a nested import: %s' % new)
return new return new

View File

@@ -8,6 +8,7 @@ import sys
import os import os
import time import time
import cache
import parsing import parsing
import builtin import builtin
import debug import debug
@@ -215,7 +216,7 @@ class ModuleWithCursor(Module):
return self._part_parser return self._part_parser
@evaluate.memoize_default([]) @cache.memoize_default([])
def sys_path_with_modifications(module): def sys_path_with_modifications(module):
def execute_code(code): def execute_code(code):
c = "import os; from os.path import *; result=%s" c = "import os; from os.path import *; result=%s"