diff --git a/dynamic.py b/dynamic.py index 21ed4e54..ea1a903e 100644 --- a/dynamic.py +++ b/dynamic.py @@ -111,12 +111,16 @@ def search_params(param): return result - def check_array_additions(array): """ Just a mapper function for the internal _check_array_additions """ + if array._array.type not in ['list', 'tuple']: + # TODO also check for dict updates + return [] + is_list = array._array.type == 'list' current_module = array._array.parent_stmt().get_parent_until() - return _check_array_additions(array, current_module, is_list) + res = _check_array_additions(array, current_module, is_list) + return res @evaluate.memoize_default([]) @@ -154,7 +158,16 @@ def _check_array_additions(compare_array, module, is_list): position = c.parent_stmt().start_pos scope = c.parent_stmt().parent() + + # Special assignments should not be evaluated in this case. This + # would cause big recursion problems, because in cases like the + # code of jedi itself, += something is called and this call leads + # to many other things including params, which are not defined. + # This would lead again to dynamic param completion, and so on. + # In the end the definition is needed, and that's not with `+=`. + settings.evaluate_special_assignments = False found = evaluate.follow_call_path(backtrack_path, scope, position) + settings.evaluate_special_assignments = True if not compare_array in found: # Check if the original scope is an execution. If it is, one # can search for the same statement, that is in the module @@ -244,37 +257,20 @@ class ArrayInstance(parsing.Base): lists/sets are too complicated too handle that. """ items = [] - #print 'ic', self.var_args, self.var_args.parent_stmt() - stmt = self.var_args.parent_stmt() - #if evaluate.follow_statement.push_stmt(stmt): - # check recursion - #return [] - #if stmt.get_parent_until() == builtin.Builtin.scope: - #evaluate.follow_statement.push(stmt) for array in evaluate.follow_call_list(self.var_args): if isinstance(array, evaluate.Instance) and len(array.var_args): temp = array.var_args[0][0] if isinstance(temp, ArrayInstance): - #print items, self, id(self.var_args), id(self.var_args.parent_stmt()), array # prevent recursions # TODO compare Modules - #if evaluate.follow_statement.push_stmt(stmt): - # check recursion - # continue if self.var_args.start_pos != temp.var_args.start_pos: items += temp.iter_content() else: debug.warning('ArrayInstance recursion', self.var_args) - print 'yippie' - #evaluate.follow_statement.pop_stmt() continue items += evaluate.get_iterator_types([array]) - #if stmt.get_parent_until() == builtin.Builtin.scope: - #evaluate.follow_statement.pop() - #print 'ic finish', self.var_args module = self.var_args.parent_stmt().get_parent_until() is_list = str(self.instance.name) == 'list' items += _check_array_additions(self.instance, module, is_list) - #evaluate.follow_statement.pop_stmt() return items diff --git a/evaluate.py b/evaluate.py index 44824e8f..e5dd8679 100644 --- a/evaluate.py +++ b/evaluate.py @@ -30,6 +30,7 @@ import builtin import imports import helpers import dynamic +import settings memoize_caches = [] statement_path = [] @@ -407,7 +408,7 @@ class Function(parsing.Base): return f def get_decorated_func(self): - if self._decorated_func == None: + if self._decorated_func is None: raise DecoratorNotFound() if self._decorated_func == self.base_func: return self @@ -723,6 +724,7 @@ class Generator(parsing.Base): def iter_content(self): """ returns the content of __iter__ """ + #print self, follow_statement.node_statements() return Execution(self.func, self.var_args).get_return_types(True) def get_index_types(self, index=None): @@ -833,7 +835,7 @@ class ArrayElement(object): def __getattr__(self, name): # Set access privileges: - if name not in ['parent', 'names', 'start_pos', 'end_pos']: + if name not in ['parent', 'names', 'start_pos', 'end_pos', 'get_code']: raise AttributeError('Strange access: %s.' % name) return getattr(self.name, name) @@ -848,12 +850,13 @@ def get_defined_names_for_position(obj, position=None, start_scope=None): names = obj.get_defined_names() # Instances have special rules, always return all the possible completions, # because class variables are always valid and the `self.` variables, too. - if not position or isinstance(obj, Instance) or start_scope != obj \ + if not position or isinstance(obj, (Instance, Array)) \ + or start_scope != obj \ and isinstance(start_scope, (parsing.Function, Execution)): return names names_new = [] for n in names: - if (n.start_pos) < position: + if n.start_pos < position: names_new.append(n) return names_new @@ -1089,7 +1092,7 @@ def get_iterator_types(inputs): # __iter__ returned an instance. name = '__next__' if is_py3k() else 'next' try: - result += it.execute_subscope_by_name(name) + result += gen.execute_subscope_by_name(name) except KeyError: debug.warning('Instance has no __next__ function', gen) else: @@ -1152,8 +1155,12 @@ def assign_tuples(tup, results, seek_name): def follow_statement(stmt, seek_name=None): """ :param stmt: contains a statement - :param scope: contains a scope. If not given, takes the parent of stmt. """ + if not settings.evaluate_special_assignments: + det = stmt.assignment_details + if det and det[0][0] != '=': + return [] + statement_path.append(stmt) # important to know for the goto function debug.dbg('follow_stmt %s (%s)' % (stmt, seek_name)) @@ -1225,6 +1232,9 @@ def follow_call(call): """ Follow a call is following a function, variable, string, etc. """ scope = call.parent_stmt().parent() path = call.generate_call_path() + path = list(path) + #print 'p', scope, path + path = iter(path) position = call.parent_stmt().start_pos return follow_call_path(path, scope, position) diff --git a/settings.py b/settings.py index 2a717715..debd376e 100644 --- a/settings.py +++ b/settings.py @@ -1,4 +1,14 @@ +# ---------------- +# global settings +# ---------------- dynamic_arrays_instances = True dynamic_array_additions = True dynamic_params = True + +# ---------------- +# internally used: +# ---------------- + +# evaluation of +=, -=, /=, etc. +evaluate_special_assignments = True diff --git a/test/completion/thirdparty/jedi_.py b/test/completion/thirdparty/jedi_.py index 66aa154a..dd678edd 100644 --- a/test/completion/thirdparty/jedi_.py +++ b/test/completion/thirdparty/jedi_.py @@ -14,21 +14,21 @@ scopes, path, dot, like = \ source_path, True) # has problems with that (sometimes) very deep nesting. -##? set() +#? set() el = scopes # get_names_for_scope is also recursion stuff -##? tuple() +#? tuple() el = list(evaluate.get_names_for_scope())[0] -##? int() parsing.Module() +#? int() parsing.Module() el = list(evaluate.get_names_for_scope(1))[0][0] #? parsing.Module() el = list(evaluate.get_names_for_scope())[0][0] -##? list() +#? list() el = list(evaluate.get_names_for_scope(1))[0][1] -##? list() +#? list() el = list(evaluate.get_names_for_scope())[0][1] # TODO here should stand evaluate.Instance() and so on. diff --git a/test/completion/types.py b/test/completion/types.py index 932555e3..0f509598 100644 --- a/test/completion/types.py +++ b/test/completion/types.py @@ -36,6 +36,9 @@ arr2 = [1,2,3] #? ['append'] arr2.app +#? int() +arr.count(1) + # ----------------- # dicts # -----------------