From 37f2b8ff56a1918335bd76dbedeea2cea6ee9677 Mon Sep 17 00:00:00 2001 From: David Halter Date: Mon, 9 Jul 2012 12:05:07 +0200 Subject: [PATCH] recursions are now being catched --- evaluate.py | 11 ++++++-- helpers.py | 45 ++++++++++++++++++++++++++++++ test/completion/functions.py | 17 +++++++++-- test/completion/thirdparty/jedi.py | 2 +- 4 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 helpers.py diff --git a/evaluate.py b/evaluate.py index be452ee7..5111b671 100644 --- a/evaluate.py +++ b/evaluate.py @@ -30,6 +30,7 @@ import parsing import modules import debug import builtin +import helpers memoize_caches = [] @@ -70,6 +71,8 @@ def clear_caches(): for m in memoize_caches: m.clear() + #follow_statement.reset() + def memoize_default(default=None): """ @@ -209,7 +212,8 @@ class Instance(Executable): def __getattr__(self, name): if name not in ['line_nr', 'indent', 'name', 'get_imports']: - raise AttributeError("Don't touch this (%s)!" % name) + raise AttributeError("Instance %s: Don't touch this (%s)!" + % (self, name)) return getattr(self.base, name) def __repr__(self): @@ -976,7 +980,9 @@ def assign_tuples(tup, results, seek_name): if hasattr(r, "get_exact_index_types"): types += r.get_exact_index_types(index) else: - debug.warning("assign tuples: invalid tuple lookup") + debug.warning("invalid tuple lookup %s of result %s in %s" + % (tup, results, seek_name)) + return types result = [] @@ -1002,6 +1008,7 @@ def assign_tuples(tup, results, seek_name): return result +@helpers.RecursionDecorator @memoize_default(default=[]) def follow_statement(stmt, scope=None, seek_name=None): """ diff --git a/helpers.py b/helpers.py new file mode 100644 index 00000000..d715bade --- /dev/null +++ b/helpers.py @@ -0,0 +1,45 @@ +import parsing + +class RecursionDecorator(object): + """ A decorator to detect recursions in statements """ + def __init__(self, func): + self.func = func + self.reset() + self.current = None + + def __call__(self, stmt, *args, **kwargs): + # don't check param instances, they are not causing recursions + if isinstance(stmt, parsing.Param): + return self.func(stmt, *args, **kwargs) + + r = RecursionNode(stmt, self.current) + if self.check_recursion(r): + return [] + parent, self.current = self.current, r + result = self.func(stmt, *args, **kwargs) + self.current = parent + return result + + def check_recursion(self, new): + test = self.current + while True: + if new == test: + return True + if not test: + return False + test = test.parent + + def reset(self): + self.top = None + self.current = None + +class RecursionNode(object): + def __init__(self, stmt, parent): + self.script = stmt.get_parent_until() + self.position = (stmt.line_nr, stmt.indent) + self.parent = parent + + def __eq__(self, other): + if not other: + return None + return self.script == other.script and self.position == other.position diff --git a/test/completion/functions.py b/test/completion/functions.py index 2fd468d9..bdab15a6 100644 --- a/test/completion/functions.py +++ b/test/completion/functions.py @@ -54,7 +54,7 @@ variable_rename(list())(). variable_rename(1)(). # ----------------- -# recursion (should ignore) +# recursions (should ignore) # ----------------- def recursion(a, b): if a: @@ -62,9 +62,22 @@ def recursion(a, b): else: return recursion(a+".", b+1) -##? int() float() +#? int() float() recursion("a", 1.0) +def other(a): + return recursion2(a) + +def recursion2(a): + if a: + return other(a) + else: + return recursion2("") + return a + +#? int() str() str() +recursion2(1) + # ----------------- # ordering # ----------------- diff --git a/test/completion/thirdparty/jedi.py b/test/completion/thirdparty/jedi.py index 5892efe2..d0f524d9 100644 --- a/test/completion/thirdparty/jedi.py +++ b/test/completion/thirdparty/jedi.py @@ -17,5 +17,5 @@ scopes, path, dot, like = \ #? set() el = scopes. -##? str() <--- recursion +#? str() <--- recursion el = evaluate.get_names_for_scope()[0].