From 9a25d55953b804b4a21238b17bc27f7fd0378f51 Mon Sep 17 00:00:00 2001 From: ColinDuquesnoy Date: Sun, 13 Dec 2015 20:08:48 +0100 Subject: [PATCH 1/2] Add regression test for issue #591 See #649 --- test/test_regression.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/test_regression.py b/test/test_regression.py index 94132acd..9e673154 100644 --- a/test/test_regression.py +++ b/test/test_regression.py @@ -156,6 +156,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_loading_unicode_files_with_bad_global_charset(monkeypatch, tmpdir): dirname = str(tmpdir.mkdir('jedi-test')) From 6e3b5dfb23ca054545638f07c3ca75d6356a20c9 Mon Sep 17 00:00:00 2001 From: ColinDuquesnoy Date: Sun, 13 Dec 2015 20:10:09 +0100 Subject: [PATCH 2/2] Add memoize_function to cache and use it in fake.get_faked The previously added test should now pass. Fix #591 --- jedi/cache.py | 12 ++++++++++++ jedi/evaluate/compiled/fake.py | 2 ++ 2 files changed, 14 insertions(+) diff --git a/jedi/cache.py b/jedi/cache.py index 56769d0d..fe073352 100644 --- a/jedi/cache.py +++ b/jedi/cache.py @@ -163,6 +163,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 0037cfc7..32c28b66 100644 --- a/jedi/evaluate/compiled/fake.py +++ b/jedi/evaluate/compiled/fake.py @@ -8,6 +8,7 @@ import os import inspect from jedi._compatibility import is_py3, builtins, unicode +from jedi.cache import memoize_function from jedi.parser import Parser, load_grammar from jedi.parser import tree as pt from jedi.evaluate.helpers import FakeName @@ -97,6 +98,7 @@ def _faked(module, obj, name): return search_scope(cls, name) +@memoize_function def get_faked(module, obj, name=None): obj = obj.__class__ if is_class_instance(obj) else obj result = _faked(module, obj, name)