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()
def __del__(self):
evaluate.clear_caches()
api_classes._clear_caches()
def set_debug_function(func_cb=debug.print_to_stdout, warnings=True,

View File

@@ -3,6 +3,9 @@
import re
import os
import cache
import dynamic
import helpers
import settings
import evaluate
import imports
@@ -10,6 +13,20 @@ import parsing
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):
_mapping = {'posixpath': 'os.path',
'riscospath': 'os.path',
@@ -193,7 +210,7 @@ class Completion(BaseDefinition):
self._followed_definitions = \
[BaseDefinition(d, d.start_pos) for d in defs]
evaluate.clear_caches()
_clear_caches()
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
#@evaluate.memoize_default() # TODO add
#@cache.memoize_default() # TODO add
def follow_param(param):
func = param.parent_function
#print func, param, param.parent_function

View File

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

View File

@@ -18,6 +18,7 @@ import sys
import itertools
import copy
import cache
import parsing
import debug
import builtin
@@ -26,9 +27,6 @@ import helpers
import dynamic
import docstrings
memoize_caches = []
faked_scopes = []
class DecoratorNotFound(LookupError):
"""
@@ -63,66 +61,6 @@ class MultiLevelAttributeError(Exception):
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):
""" An instance is also an executable - because __init__ is called """
def __init__(self, base, var_args=None):
@@ -140,7 +78,7 @@ class Executable(parsing.Base):
return self.base.parent
class Instance(use_metaclass(CachedMetaClass, Executable)):
class Instance(use_metaclass(cache.CachedMetaClass, Executable)):
""" This class is used to evaluate instances. """
def __init__(self, base, var_args=None):
super(Instance, self).__init__(base, var_args)
@@ -159,7 +97,7 @@ class Instance(use_metaclass(CachedMetaClass, Executable)):
# (No var_args) used.
self.is_generated = False
@memoize_default()
@cache.memoize_default()
def get_init_execution(self, func):
func = InstanceElement(self, func, True)
return Execution(func, self.var_args)
@@ -227,7 +165,7 @@ class Instance(use_metaclass(CachedMetaClass, Executable)):
args = helpers.generate_param_array(v)
return self.execute_subscope_by_name('__get__', args)
@memoize_default([])
@cache.memoize_default([])
def get_defined_names(self):
"""
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 []))
class InstanceElement(use_metaclass(CachedMetaClass)):
class InstanceElement(use_metaclass(cache.CachedMetaClass)):
"""
InstanceElement is a wrapper for any object, that is used as an instance
variable (e.g. self.variable or class methods).
@@ -288,7 +226,7 @@ class InstanceElement(use_metaclass(CachedMetaClass)):
self.is_class_var = is_class_var
@property
@memoize_default()
@cache.memoize_default()
def parent(self):
par = self.var.parent
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,
self.is_class_var)
new.parent_stmt = par
faked_scopes.append(par)
faked_scopes.append(new)
cache.faked_scopes.append(par)
cache.faked_scopes.append(new)
return new
def __getattr__(self, name):
@@ -331,7 +269,7 @@ class InstanceElement(use_metaclass(CachedMetaClass)):
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
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):
self.base = base
@memoize_default(default=[])
@cache.memoize_default(default=[])
def get_super_classes(self):
supers = []
# 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')
return supers
@memoize_default(default=[])
@cache.memoize_default(default=[])
def get_defined_names(self):
def in_iterable(name, 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)
class Function(use_metaclass(CachedMetaClass, parsing.Base)):
class Function(use_metaclass(cache.CachedMetaClass, parsing.Base)):
"""
Needed because of decorators. Decorators are evaluated here.
"""
@@ -408,7 +346,7 @@ class Function(use_metaclass(CachedMetaClass, parsing.Base)):
self.is_decorated = is_decorated
@property
@memoize_default()
@cache.memoize_default()
def _decorated_func(self):
"""
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.
old_func = Function(f, is_decorated=True)
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()
if not len(wrappers):
@@ -481,7 +419,7 @@ class Execution(Executable):
multiple calls to functions and recursion has to be avoided. But this is
responsibility of the decorators.
"""
@memoize_default(default=[])
@cache.memoize_default(default=[])
@helpers.ExecutionRecursionDecorator
def get_return_types(self, evaluate_generator=False):
""" Get the return types of a function. """
@@ -555,7 +493,7 @@ class Execution(Executable):
stmts += follow_statement(r)
return stmts
@memoize_default(default=[])
@cache.memoize_default(default=[])
def get_params(self):
"""
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
name = copy.copy(param.get_name())
name.parent = new_param
faked_scopes.append(new_param)
cache.faked_scopes.append(new_param)
return name
result = []
@@ -737,7 +675,7 @@ class Execution(Executable):
if isinstance(copied, parsing.Function):
copied = Function(copied)
objects.append(copied)
faked_scopes.append(copied)
cache.faked_scopes.append(copied)
return objects
def __getattr__(self, name):
@@ -745,7 +683,7 @@ class Execution(Executable):
raise AttributeError('Tried to access %s: %s. Why?' % (name, self))
return getattr(self.base, name)
@memoize_default()
@cache.memoize_default()
def _scope_copy(self, scope):
try:
""" Copies a scope (e.g. if) in an execution """
@@ -758,28 +696,28 @@ class Execution(Executable):
else:
copied = helpers.fast_parent_copy(scope)
copied.parent = self._scope_copy(copied.parent)
faked_scopes.append(copied)
cache.faked_scopes.append(copied)
return copied
except AttributeError:
raise MultiLevelAttributeError(sys.exc_info())
@property
@memoize_default()
@cache.memoize_default()
def returns(self):
return self.copy_properties('returns')
@property
@memoize_default()
@cache.memoize_default()
def asserts(self):
return self.copy_properties('asserts')
@property
@memoize_default()
@cache.memoize_default()
def statements(self):
return self.copy_properties('statements')
@property
@memoize_default()
@cache.memoize_default()
def subscopes(self):
return self.copy_properties('subscopes')
@@ -791,7 +729,7 @@ class Execution(Executable):
(type(self).__name__, self.base)
class Generator(use_metaclass(CachedMetaClass, parsing.Base)):
class Generator(use_metaclass(cache.CachedMetaClass, parsing.Base)):
""" Cares for `yield` statements. """
def __init__(self, func, var_args):
super(Generator, self).__init__()
@@ -830,7 +768,7 @@ class Generator(use_metaclass(CachedMetaClass, parsing.Base)):
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
methods which are important in this module.
@@ -1380,7 +1318,7 @@ def assign_tuples(tup, results, seek_name):
@helpers.RecursionDecorator
@memoize_default(default=[])
@cache.memoize_default(default=[])
def follow_statement(stmt, seek_name=None):
"""
The starting point of the completion. A statement always owns a call list,

View File

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

View File

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

View File

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