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
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):
@time_cache("star_import_cache_validity")
def wrapper(self):

View File

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

View File

@@ -9,7 +9,6 @@ import inspect
import types
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 tree as pt
from jedi.evaluate.helpers import FakeName
@@ -44,6 +43,10 @@ if is_py3:
NOT_CLASS_TYPES += (types.DynamicClassAttribute,)
class FakeDoesNotExist(Exception):
pass
def _load_faked_module(module):
module_name = module.__name__
if module_name == '__builtin__' and not is_py3:
@@ -143,13 +146,35 @@ def _faked(module, obj, name):
return search_scope(cls, name)
@memoize_function
def get_faked(module, obj, name=None):
def memoize_faked(obj):
"""
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
result = _faked(module, obj, name)
if result is None or isinstance(result, pt.Class):
# We're not interested in classes. What we want is functions.
return None
raise FakeDoesNotExist
else:
# Set the docstr which was previously not set (faked modules don't
# contain it).
@@ -162,6 +187,12 @@ def get_faked(module, obj, name=None):
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):
"""Like inspect.* methods."""
try: