Use the memoize function for faked arguments only when needed.

It's important to note that memoizing every object would mean that
theoretically all objects passed through get_faked would get memoized. This
would have been a possible memory leak, which we should avoid.
Obviously the previous solution proposed in #649 was still better, but this
issue was a new one. Also using str() around keys was not a good idea.

Refs #649.
This commit is contained in:
Dave Halter
2016-07-31 15:02:30 +02:00
parent 7b58ffcfd1
commit 6f598b1157
3 changed files with 50 additions and 30 deletions

View File

@@ -91,18 +91,6 @@ def memoize_method(method):
return wrapper return wrapper
def memoize_function(obj):
""" A normal memoize function for memoizing free functions. """
cache = obj.cache = {}
def memoizer(*args, **kwargs):
key = str(args) + str(kwargs)
if key not in cache:
cache[key] = obj(*args, **kwargs)
return cache[key]
return memoizer
def cache_star_import(func): def cache_star_import(func):
@time_cache("star_import_cache_validity") @time_cache("star_import_cache_validity")
def wrapper(self): def wrapper(self):

View File

@@ -222,10 +222,12 @@ class CompiledObject(Base):
module = self.get_parent_until() module = self.get_parent_until()
faked_subscopes = [] faked_subscopes = []
for name in dir(self.obj): for name in dir(self.obj):
f = fake.get_faked(module.obj, self.obj, name) try:
if f: faked_subscopes.append(
f.parent = self fake.get_faked(module.obj, self.obj, parent=self, name=name)
faked_subscopes.append(f) )
except fake.FakeDoesNotExist:
pass
return faked_subscopes return faked_subscopes
def is_scope(self): def is_scope(self):
@@ -445,16 +447,15 @@ def _parse_function_doc(doc):
def _create_from_name(evaluator, module, parent, name): def _create_from_name(evaluator, module, parent, name):
faked = fake.get_faked(module.obj, parent.obj, name) try:
# only functions are necessary. return fake.get_faked(module.obj, parent.obj, parent=parent, name=name)
if faked is not None: except fake.FakeDoesNotExist:
faked.parent = parent pass
return faked
try: try:
obj = getattr(parent.obj, name) obj = getattr(parent.obj, name)
except AttributeError: except AttributeError:
# happens e.g. in properties of # Happens e.g. in properties of
# PyQt4.QtGui.QStyleOptionComboBox.currentText # PyQt4.QtGui.QStyleOptionComboBox.currentText
# -> just set it to None # -> just set it to None
obj = None obj = None
@@ -527,9 +528,9 @@ def create(evaluator, obj, parent=None, module=None):
if parent is None and obj != _builtins: if parent is None and obj != _builtins:
return create(evaluator, obj, create(evaluator, _builtins)) return create(evaluator, obj, create(evaluator, _builtins))
faked = fake.get_faked(module and module.obj, obj) try:
if faked is not None: return fake.get_faked(module and module.obj, obj, parent=parent)
faked.parent = parent except fake.FakeDoesNotExist:
return faked pass
return CompiledObject(evaluator, obj, parent) return CompiledObject(evaluator, obj, parent)

View File

@@ -9,7 +9,6 @@ import inspect
import types import types
from jedi._compatibility import is_py3, builtins, unicode, is_py34 from jedi._compatibility import is_py3, builtins, unicode, is_py34
from jedi.cache import memoize_function
from jedi.parser import ParserWithRecovery, load_grammar from jedi.parser import ParserWithRecovery, load_grammar
from jedi.parser import tree as pt from jedi.parser import tree as pt
from jedi.evaluate.helpers import FakeName from jedi.evaluate.helpers import FakeName
@@ -44,6 +43,10 @@ if is_py3:
NOT_CLASS_TYPES += (types.DynamicClassAttribute,) NOT_CLASS_TYPES += (types.DynamicClassAttribute,)
class FakeDoesNotExist(Exception):
pass
def _load_faked_module(module): def _load_faked_module(module):
module_name = module.__name__ module_name = module.__name__
if module_name == '__builtin__' and not is_py3: if module_name == '__builtin__' and not is_py3:
@@ -143,13 +146,35 @@ def _faked(module, obj, name):
return search_scope(cls, name) return search_scope(cls, name)
@memoize_function def memoize_faked(obj):
def get_faked(module, obj, name=None): """
A typical memoize function that ignores issues with non hashable results.
"""
cache = obj.cache = {}
def memoizer(*args, **kwargs):
key = (obj, args, frozenset(kwargs.items()))
try:
result = cache[key]
except TypeError:
return obj(*args, **kwargs)
except KeyError:
result = obj(*args, **kwargs)
if result is not None:
cache[key] = obj(*args, **kwargs)
return result
else:
return result
return memoizer
@memoize_faked
def _get_faked(module, obj, name=None):
obj = type(obj) if is_class_instance(obj) else obj obj = type(obj) if is_class_instance(obj) else obj
result = _faked(module, obj, name) result = _faked(module, obj, name)
if result is None or isinstance(result, pt.Class): if result is None or isinstance(result, pt.Class):
# We're not interested in classes. What we want is functions. # We're not interested in classes. What we want is functions.
return None raise FakeDoesNotExist
else: else:
# Set the docstr which was previously not set (faked modules don't # Set the docstr which was previously not set (faked modules don't
# contain it). # contain it).
@@ -162,6 +187,12 @@ def get_faked(module, obj, name=None):
return result return result
def get_faked(module, obj, name=None, parent=None):
faked = _get_faked(module, obj, name)
faked.parent = parent
return faked
def is_class_instance(obj): def is_class_instance(obj):
"""Like inspect.* methods.""" """Like inspect.* methods."""
try: try: