diff --git a/jedi/cache.py b/jedi/cache.py index 16d8a72b..7535a774 100644 --- a/jedi/cache.py +++ b/jedi/cache.py @@ -102,6 +102,37 @@ def cache_call_signatures(stmt): return None if module_path is None else (module_path, stmt.start_pos) +def underscore_memoization(func): + """ + Decorator for methods:: + + class A(object): + def x(self): + if self._x: + self._x = 10 + return self._x + + Becomes:: + + class A(object): + @underscore_memoization + def x(self): + return 10 + + A now has an attribute ``_x`` written by this decorator. + """ + def wrapper(self): + name = '_' + func.__name__ + try: + return getattr(self, name) + except AttributeError: + result = func(self) + setattr(self, name, result) + return result + + return wrapper + + def cache_star_import(func): def wrapper(evaluator, scope, *args, **kwargs): with common.ignored(KeyError): diff --git a/jedi/modules.py b/jedi/modules.py index 03ec4e9a..fbb6c5af 100644 --- a/jedi/modules.py +++ b/jedi/modules.py @@ -67,23 +67,20 @@ class ModuleWithCursor(object): self.position = position self._path_until_cursor = None self._line_cache = None - self._parser = None # this two are only used, because there is no nonlocal in Python 2 self._line_temp = None self._relevant_temp = None @property + @cache.underscore_memoization def parser(self): """ get the parser lazy """ - if not self._parser: - cache.invalidate_star_import_cache(self.path) - self._parser = fast.FastParser(self.source, self.path, self.position) - # don't pickle that module, because the main module is changing - # quickly usually. - cache.save_parser(self.path, self.name, self._parser, - pickling=False) - return self._parser + cache.invalidate_star_import_cache(self.path) + parser = fast.FastParser(self.source, self.path, self.position) + # Don't pickle that module, because the main module is changing quickly + cache.save_parser(self.path, self.name, parser, pickling=False) + return parser def get_path_until_cursor(self): """ Get the path under the cursor. """