From c6f76a15ffb9c5bd077e10f03358057857a40f3a Mon Sep 17 00:00:00 2001 From: David Halter Date: Thu, 9 Aug 2012 14:21:51 +0200 Subject: [PATCH] dynamic array additions are working with list.append --- dynamic.py | 64 ++++++++++++++++++++++++++++++++++++++ evaluate.py | 11 +++++-- functions.py | 1 + mixin/builtins.py | 6 ++++ parsing.py | 7 +++-- test/completion/arrays.py | 7 +++++ test/completion/dynamic.py | 38 ++++++++++++++++++++++ 7 files changed, 129 insertions(+), 5 deletions(-) diff --git a/dynamic.py b/dynamic.py index 2b50b0b0..df10026d 100644 --- a/dynamic.py +++ b/dynamic.py @@ -1,6 +1,8 @@ """ For dynamic completion. """ +import copy + import parsing import evaluate @@ -79,3 +81,65 @@ def search_params(param): func.listeners.remove(listener) return result + + +@evaluate.memoize_default([]) +def check_array_additions(array, is_list=True): + """ + Checks if a `parsing.Array` has "add" statements: + >>> a = [""] + >>> a.append(1) + """ + def scan_array(arr, search_name): + """ Returns the function Calls that match func_name """ + result = [] + for sub in arr: + for s in sub: + if isinstance(s, parsing.Array): + result += scan_array(s, search_name) + elif isinstance(s, parsing.Call): + n = s.name + if isinstance(n, parsing.Name) and search_name in n.names: + result.append(s) + return result + + def check_calls(calls, add_name): + result = [] + for c in calls: + call_path = list(c.generate_call_path()) + separate_index = call_path.index(add_name) + if not len(call_path) > separate_index + 1: + # this means that there is no execution -> [].append + continue + backtrack_path = iter(call_path[:separate_index]) + + position = c.parent_stmt.start_pos + scope = c.parent_stmt.parent + print 'd', call_path + e = evaluate.follow_call_path(backtrack_path, scope, position) + print 'e', e + if not array in e: + # the `append`, etc. belong to other arrays + continue + + if add_name in ['append', 'add']: + result += evaluate.follow_call_list(call_path[separate_index + 1]) + elif add_name in ['extend', 'update']: + result += evaluate.follow_call_list(call_path[separate_index + 1]) + return result + + stmt = array._array.parent_stmt + current_module = stmt.get_parent_until() + search_names = ['append', 'extend', 'insert'] if is_list else \ + ['add', 'update'] + possible_stmts = [] + result = [] + for n in search_names: + try: + possible_stmts += current_module.used_names[n] + except KeyError: + continue + for stmt in possible_stmts: + result += check_calls(scan_array(stmt.get_assignment_calls(), n), n) + + return result diff --git a/evaluate.py b/evaluate.py index 5aa3f585..cd042e74 100644 --- a/evaluate.py +++ b/evaluate.py @@ -1171,9 +1171,14 @@ def follow_call_list(call_list): def follow_call(call): """ Follow a call is following a function, variable, string, etc. """ scope = call.parent_stmt.parent - path = call.generate_call_list() + path = call.generate_call_path() position = call.parent_stmt.start_pos + return follow_call_path(path, scope, position) + + +def follow_call_path(path, scope, position): + """ Follows a path generated by `parsing.Call.generate_call_path()` """ current = next(path) if isinstance(current, parsing.Array): @@ -1239,7 +1244,9 @@ def follow_path(path, scope, position=None): if isinstance(current, parsing.Array): # This must be an execution, either () or []. if current.type == parsing.Array.LIST: - result = scope.get_index_types(current) + result = list(scope.get_index_types(current)) + if isinstance(scope, Array): + result += dynamic.check_array_additions(scope) elif current.type not in [parsing.Array.DICT]: # Scope must be a class or func - make an instance or execution. debug.dbg('exe', scope) diff --git a/functions.py b/functions.py index 6eae67f1..82733958 100644 --- a/functions.py +++ b/functions.py @@ -1,6 +1,7 @@ import re import parsing +import dynamic # must be before evaluate, because it needs to be loaded first. import evaluate import modules import debug diff --git a/mixin/builtins.py b/mixin/builtins.py index e4e986a9..e7a6be63 100644 --- a/mixin/builtins.py +++ b/mixin/builtins.py @@ -137,6 +137,12 @@ class frozenset(): def copy(self): return self +class tuple(): + def index(self): + return 1 + + def count(self): + return 1 #-------------------------------------------------------- # basic types diff --git a/parsing.py b/parsing.py index b5632aff..a1876053 100644 --- a/parsing.py +++ b/parsing.py @@ -657,6 +657,7 @@ class Statement(Simple): c_type = Call.NUMBER if is_chain: + #print 'chain', self, tok, result call = Call(tok, c_type, parent=result) result = result.set_next_chain_call(call) is_chain = False @@ -807,17 +808,17 @@ class Call(object): call.parent = self return call - def generate_call_list(self): + def generate_call_path(self): try: for name_part in self.name.names: yield name_part except AttributeError: yield self if self.execution is not None: - for y in self.execution.generate_call_list(): + for y in self.execution.generate_call_path(): yield y if self.next is not None: - for y in self.next.generate_call_list(): + for y in self.next.generate_call_path(): yield y def __repr__(self): diff --git a/test/completion/arrays.py b/test/completion/arrays.py index 23670d57..65644b42 100644 --- a/test/completion/arrays.py +++ b/test/completion/arrays.py @@ -162,6 +162,13 @@ dic2 = {'asdf': 3, 'b': 'str'} #? int() dic2[index] +# ----------------- +# chaining +# ----------------- + +#? int() +[tuple()].index() + # ----------------- # __getitem__ # ----------------- diff --git a/test/completion/dynamic.py b/test/completion/dynamic.py index c68d6759..ee842b13 100644 --- a/test/completion/dynamic.py +++ b/test/completion/dynamic.py @@ -74,8 +74,46 @@ class A(): def __init__(self, a): #? int() a + self.a = a + def test(self, a): #? float() a + def test2(self): + ##? int() + self.a + A(3).test(2.0) +A(3).test2() + +# ----------------- +# list.append/insert +# ----------------- +arr = [] +for a in [1,2]: + arr.append(a); + +arr.append # should not cause an exception + +#? int() +arr[10] + +arr = [tuple()] +for a in [1,2]: + arr.append(a); + +#? int() tuple() +arr[10] +#? int() +arr[10].index() + +arr = [""] +arr.insert(0, 1.0) +#? float() str() +arr[10] + + +# ----------------- +# set.append +# -----------------