diff --git a/jedi/cache.py b/jedi/cache.py index 8dc82544..5f618375 100644 --- a/jedi/cache.py +++ b/jedi/cache.py @@ -91,6 +91,18 @@ 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): diff --git a/jedi/evaluate/compiled/fake.py b/jedi/evaluate/compiled/fake.py index 56eb2e28..9a422c11 100644 --- a/jedi/evaluate/compiled/fake.py +++ b/jedi/evaluate/compiled/fake.py @@ -9,6 +9,7 @@ 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 @@ -142,6 +143,7 @@ def _faked(module, obj, name): return search_scope(cls, name) +@memoize_function def get_faked(module, obj, name=None): obj = type(obj) if is_class_instance(obj) else obj result = _faked(module, obj, name) diff --git a/test/test_regression.py b/test/test_regression.py index 48909798..abb10727 100644 --- a/test/test_regression.py +++ b/test/test_regression.py @@ -157,6 +157,29 @@ class TestRegression(TestCase): "abc()." assert Script(s).completions() + def test_fake_subnodes(self): + """ + Test the number of subnodes of a fake object. + + There was a bug where the number of child nodes would grow on every + call to :func:``jedi.evaluate.compiled.fake.get_faked``. + + See Github PR#649 and isseu #591. + """ + def get_str_completion(values): + for c in values: + if c.name == 'str': + return c + limit = None + for i in range(2): + completions = Script('').completions() + c = get_str_completion(completions) + n = len(c._definition.subscopes[0].children[-1].children) + if i == 0: + limit = n + else: + assert n == limit + def test_source_to_unicode_unicode_text(self): source = ( b"# vim: fileencoding=utf-8\n"