From 7cf70a3f0a79ced8fb2e9e847dc52abe0b8af224 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Fri, 15 Mar 2013 00:21:46 +0100 Subject: [PATCH] Do not clear cache in __del__ Prior to this change, running `py.test --assert=rewrite test/test_integration.py` fails with errors due to: ```py cls = @classmethod def cleanup(cls): > cls.parent_execution_funcs.pop() E IndexError: pop from empty list recursion.py:127: IndexError ``` Similar errors occurred in travis occasionally: - https://travis-ci.org/davidhalter/jedi/jobs/5449831 - https://travis-ci.org/davidhalter/jedi/jobs/5512538 I think this is because GC calls __del__ at random point during actual execution of ExecutionRecursionDecorator.__call__. As --assert=rewrite works by AST dynamically, I guess that it is harder for Python's GC to clean up code and therefore GC happens sometime later. Although this is just a random guess, as `tox -- --assert=rewrite` works with this patch now, I think this is a good change. --- jedi/api.py | 8 +++++--- jedi/api_classes.py | 13 +++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/jedi/api.py b/jedi/api.py index 9de6d8ac..86b9a369 100644 --- a/jedi/api.py +++ b/jedi/api.py @@ -76,6 +76,7 @@ class Script(object): """ lazy parser.""" return self._module.parser + @api_classes._clear_caches_after_call def complete(self): """ Return :class:`api_classes.Completion` objects. Those objects contain @@ -209,6 +210,7 @@ class Script(object): warnings.warn("Use line instead.", DeprecationWarning) return self.definition() + @api_classes._clear_caches_after_call def definition(self): """ Return the definitions of a the path under the cursor. This is not a @@ -272,6 +274,7 @@ class Script(object): if not isinstance(s, imports.ImportPath._GlobalNamespace)]) return sorted(d, key=lambda x: (x.module_path, x.start_pos)) + @api_classes._clear_caches_after_call def goto(self): """ Return the first definition found by goto. Imports and statements @@ -334,6 +337,7 @@ class Script(object): definitions = [user_stmt] return definitions, search_name + @api_classes._clear_caches_after_call def related_names(self, additional_module_paths=()): """ Return :class:`api_classes.RelatedName` objects, which contain all @@ -378,6 +382,7 @@ class Script(object): warnings.warn("Use line instead.", DeprecationWarning) return self.function_definition() + @api_classes._clear_caches_after_call def function_definition(self): """ Return the function object of the call you're currently in. @@ -487,9 +492,6 @@ class Script(object): match = re.match(r'^(.*?)(\.|)(\w?[\w\d]*)$', path, flags=re.S) return match.groups() - def __del__(self): - api_classes._clear_caches() - def defined_names(source, source_path=None, source_encoding='utf-8'): """ diff --git a/jedi/api_classes.py b/jedi/api_classes.py index 051baa93..2be10641 100644 --- a/jedi/api_classes.py +++ b/jedi/api_classes.py @@ -7,6 +7,7 @@ interesting information about completion and goto operations. import re import os import warnings +import functools from _compatibility import unicode, next import cache @@ -34,6 +35,18 @@ def _clear_caches(): imports.imports_processed = 0 +def _clear_caches_after_call(func): + """ + Clear caches just before returning a value. + """ + @functools.wraps(func) + def wrapper(*args, **kwds): + result = func(*args, **kwds) + _clear_caches() + return result + return wrapper + + class BaseDefinition(object): _mapping = {'posixpath': 'os.path', 'riscospath': 'os.path',