From 4bc55be1036167552f4f425902c8871d5a9a4db2 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Sun, 13 Apr 2014 16:31:38 +0200 Subject: [PATCH] significant speedup due to compiled caching --- jedi/api/interpreter.py | 2 +- jedi/evaluate/__init__.py | 9 +++++---- jedi/evaluate/compiled/__init__.py | 9 ++++++++- jedi/evaluate/finder.py | 2 +- jedi/evaluate/precedence.py | 16 ++++++++-------- test/test_evaluate/test_compiled.py | 4 ++-- 6 files changed, 25 insertions(+), 17 deletions(-) diff --git a/jedi/api/interpreter.py b/jedi/api/interpreter.py index 48093046..fa6cf103 100644 --- a/jedi/api/interpreter.py +++ b/jedi/api/interpreter.py @@ -80,7 +80,7 @@ class LazyName(helpers.FakeName): mod) module = compiled.CompiledObject(raw_module) - return compiled.create(self._value, module, module) + return compiled.create(self._evaluator, self._value, module, module) @parent.setter def parent(self, value): diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index ff65ab21..5da181ff 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -89,6 +89,7 @@ class Evaluator(object): def __init__(self): self.memoize_cache = {} # for memoize decorators self.import_cache = {} # like `sys.modules`. + self.compiled_cache = {} # see `compiled.create()` self.recursion_detector = recursion.RecursionDetector() self.execution_recursion_detector = recursion.ExecutionRecursionDetector() @@ -137,10 +138,10 @@ class Evaluator(object): # only in for loops without clutter, because they are # predictable. for r in result: - left = precedence.calculate(left, operator, [r]) + left = precedence.calculate(self, left, operator, [r]) result = left else: - result = precedence.calculate(left, operator, result) + result = precedence.calculate(self, left, operator, result) elif len(stmt.get_set_vars()) > 1 and seek_name and ass_details: # Assignment checking is only important if the statement defines # multiple variables. @@ -173,7 +174,7 @@ class Evaluator(object): def _eval_precedence(self, _precedence): left = self.process_precedence_element(_precedence.left) right = self.process_precedence_element(_precedence.right) - return precedence.calculate(left, _precedence.operator, right) + return precedence.calculate(self, left, _precedence.operator, right) def _eval_statement_element(self, element): if pr.Array.is_type(element, pr.Array.NOARRAY): @@ -227,7 +228,7 @@ class Evaluator(object): search_global=True) else: # for pr.Literal - types = [compiled.create(current.value)] + types = [compiled.create(self, current.value)] types = imports.strip_imports(self, types) return self.follow_path(path, types, scope) diff --git a/jedi/evaluate/compiled/__init__.py b/jedi/evaluate/compiled/__init__.py index bc1a1977..e3220ad2 100644 --- a/jedi/evaluate/compiled/__init__.py +++ b/jedi/evaluate/compiled/__init__.py @@ -337,11 +337,18 @@ def _create_from_name(module, parent, name): return CompiledObject(obj, parent) -def create(obj, parent=builtin, module=None): +def create(evaluator, obj, parent=builtin, module=None): """ A very weird interface class to this module. The more options provided the more acurate loading compiled objects is. """ + # Do a very cheap form of caching here. + key = id(obj), id(parent), id(module) + try: + return evaluator.compiled_cache[key] + except KeyError: + pass + if not inspect.ismodule(obj): faked = fake.get_faked(module and module.obj, obj) if faked is not None: diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 2bf3c897..9ece055d 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -102,7 +102,7 @@ class NameFinder(object): """Checks for both __getattr__ and __getattribute__ methods""" result = [] # str is important to lose the NamePart! - name = compiled.create(str(self.name_str)) + name = compiled.create(self._evaluator, str(self.name_str)) with common.ignored(KeyError): result = inst.execute_subscope_by_name('__getattr__', [name]) if not result: diff --git a/jedi/evaluate/precedence.py b/jedi/evaluate/precedence.py index fab13749..18224bb4 100644 --- a/jedi/evaluate/precedence.py +++ b/jedi/evaluate/precedence.py @@ -174,12 +174,12 @@ def _check_operator(iterator, priority=PythonGrammar.LOWEST_PRIORITY): return left -def calculate(left_result, operator, right_result): +def calculate(evaluator, left_result, operator, right_result): result = [] if left_result is None and right_result: # cases like `-1` or `1 + ~1` for right in right_result: - result.append(_factor_calculate(operator, right)) + result.append(_factor_calculate(evaluator, operator, right)) return result else: if not left_result or not right_result: @@ -187,14 +187,14 @@ def calculate(left_result, operator, right_result): for left in left_result: for right in right_result: - result += _element_calculate(left, operator, right) + result += _element_calculate(evaluator, left, operator, right) return result -def _factor_calculate(operator, right): +def _factor_calculate(evaluator, operator, right): if _is_number(right): if operator == '-': - return create(-right.obj) + return create(evaluator, -right.obj) return right @@ -203,7 +203,7 @@ def _is_number(obj): and isinstance(obj.obj, (int, float)) -def _element_calculate(left, operator, right): +def _element_calculate(evaluator, left, operator, right): def is_string(obj): return isinstance(obj, CompiledObject) \ and isinstance(obj.obj, (str, unicode)) @@ -215,8 +215,8 @@ def _element_calculate(left, operator, right): return [left] elif operator == '+': if _is_number(left) and _is_number(right) or is_string(left) and is_string(right): - return [create(left.obj + right.obj)] + return [create(evaluator, left.obj + right.obj)] elif operator == '-': if _is_number(left) and _is_number(right): - return [create(left.obj - right.obj)] + return [create(evaluator, left.obj - right.obj)] return [left, right] diff --git a/test/test_evaluate/test_compiled.py b/test/test_evaluate/test_compiled.py index eb3ad4ed..3a0490c7 100644 --- a/test/test_evaluate/test_compiled.py +++ b/test/test_evaluate/test_compiled.py @@ -16,7 +16,7 @@ def test_simple(): def test_fake_loading(): - assert isinstance(compiled.create(next), Function) + assert isinstance(compiled.create(Evaluator(), next), Function) string = compiled.builtin.get_subscope_by_name('str') from_name = compiled._create_from_name( @@ -28,7 +28,7 @@ def test_fake_loading(): def test_fake_docstr(): - assert compiled.create(next).raw_doc == next.__doc__ + assert compiled.create(Evaluator(), next).raw_doc == next.__doc__ def test_parse_function_doc_illegal_docstr():