diff --git a/jedi/evaluate/context/klass.py b/jedi/evaluate/context/klass.py index acf68b67..f07ee125 100644 --- a/jedi/evaluate/context/klass.py +++ b/jedi/evaluate/context/klass.py @@ -38,7 +38,7 @@ py__doc__() Returns the docstring for a context. """ from jedi import debug from jedi._compatibility import use_metaclass -from jedi.parser_utils import get_parent_scope +from jedi.parser_utils import get_cached_parent_scope from jedi.evaluate.cache import evaluator_method_cache, CachedMetaClass, \ evaluator_method_generator_cache from jedi.evaluate import compiled @@ -106,7 +106,7 @@ class ClassFilter(ParserTreeFilter): while node is not None: if node == self._parser_scope or node == self.context: return True - node = get_parent_scope(node) + node = get_cached_parent_scope(self._used_names, node) return False def _access_possible(self, name): diff --git a/jedi/evaluate/filters.py b/jedi/evaluate/filters.py index 29b55517..a65b3c49 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -11,13 +11,13 @@ from jedi._compatibility import use_metaclass from jedi.evaluate import flow_analysis from jedi.evaluate.base_context import ContextSet, Context, ContextWrapper, \ LazyContextWrapper -from jedi.parser_utils import get_parent_scope +from jedi.parser_utils import get_cached_parent_scope from jedi.evaluate.utils import to_list -from jedi.evaluate.cache import evaluator_function_cache from jedi.evaluate.names import TreeNameDefinition, ParamName, AbstractNameDefinition _definition_name_cache = weakref.WeakKeyDictionary() + class AbstractFilter(object): _until_position = None @@ -75,8 +75,6 @@ class AbstractUsedNamesFilter(AbstractFilter): self.context = context def get(self, name): - #print(self, self.context, name, type(self).__name__) - #import traceback, sys; traceback.print_stack(file=sys.stdout) return self._convert_names(self._filter( _get_definition_names(self._used_names, name) )) @@ -124,7 +122,7 @@ class ParserTreeFilter(AbstractUsedNamesFilter): if parent.type == 'trailer': return False base_node = parent if parent.type in ('classdef', 'funcdef') else name - return get_parent_scope(base_node) == self._parser_scope + return get_cached_parent_scope(self._used_names, base_node) == self._parser_scope def _check_flows(self, names): for name in sorted(names, key=lambda name: name.start_pos, reverse=True): diff --git a/jedi/parser_utils.py b/jedi/parser_utils.py index 8d02c313..dcbd802c 100644 --- a/jedi/parser_utils.py +++ b/jedi/parser_utils.py @@ -1,6 +1,7 @@ import re import textwrap from inspect import cleandoc +from weakref import WeakKeyDictionary from parso.python import tree from parso.cache import parser_cache @@ -226,6 +227,23 @@ def is_scope(node): return t in ('file_input', 'classdef', 'funcdef', 'lambdef', 'sync_comp_for') +def _get_parent_scope_cache(func): + cache = WeakKeyDictionary() + + def wrapper(used_names, node, include_flows=False): + try: + for_module = cache[used_names] + except KeyError: + for_module = cache[used_names] = {} + + try: + return for_module[node] + except KeyError: + result = for_module[node] = func(node, include_flows) + return result + return wrapper + + def get_parent_scope(node, include_flows=False): """ Returns the underlying scope. @@ -251,6 +269,9 @@ def get_parent_scope(node, include_flows=False): return scope +get_cached_parent_scope = _get_parent_scope_cache(get_parent_scope) + + def get_cached_code_lines(grammar, path): """ Basically access the cached code lines in parso. This is not the nicest way