diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 5b2abaf9..ab328090 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -164,8 +164,9 @@ class Evaluator(object): # Iterate through result and add the values, that's possible # only in for loops without clutter, because they are # predictable. Also only do it, if the variable is not a tuple. - for_iterables = self.eval_element(for_stmt.get_input_node()) - ordered = list(iterable.py__iter__(self, for_iterables)) + node = for_stmt.get_input_node() + for_iterables = self.eval_element(node) + ordered = list(iterable.py__iter__(self, for_iterables, node)) for index_types in ordered: dct = {str(for_stmt.children[1]): index_types} diff --git a/jedi/evaluate/compiled/__init__.py b/jedi/evaluate/compiled/__init__.py index 88c5706f..88555543 100644 --- a/jedi/evaluate/compiled/__init__.py +++ b/jedi/evaluate/compiled/__init__.py @@ -198,16 +198,19 @@ class CompiledObject(Base): pass # self.obj maynot have an __iter__ method. return result + @property def py__iter__(self): if not hasattr(self.obj, '__iter__'): - debug.warning('Tried to call __getitem__ on non-iterable.') - return - if type(self.obj) not in (str, list, tuple, unicode, bytes, bytearray, dict): - # Get rid of side effects, we won't call custom `__getitem__`s. - return + raise AttributeError('No __iter__ on %s' % self.obj) - for obj in self.obj: - yield set([CompiledObject(obj)]) + def actual(): + if type(self.obj) not in (str, list, tuple, unicode, bytes, bytearray, dict): + # Get rid of side effects, we won't call custom `__getitem__`s. + return + + for obj in self.obj: + yield set([CompiledObject(obj)]) + return actual @property def name(self): diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 30c1b470..0a5d8a2e 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -301,7 +301,7 @@ def _name_to_types(evaluator, name, scope): typ = name.get_definition() if typ.isinstance(tree.ForStmt, tree.CompFor): container_types = evaluator.eval_element(typ.children[3]) - for_types = iterable.py__iter__types(evaluator, container_types) + for_types = iterable.py__iter__types(evaluator, container_types, typ.children[3]) types = check_tuple_assignments(for_types, name) elif isinstance(typ, tree.Param): types = _eval_param(evaluator, typ, scope) diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index da83fbe1..e4120e87 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -151,8 +151,8 @@ class Comprehension(IterableWrapper): return helpers.deep_ast_copy(self._get_comprehension().children[0], parent=last_comp) def py__iter__(self): - def nested(input_types, comp_fors): - iterated = py__iter__(evaluator, input_types) + def nested(input_types, comp_fors, node): + iterated = py__iter__(evaluator, input_types, node) comp_for = comp_fors[0] exprlist = comp_for.children[1] for types in iterated: @@ -160,7 +160,7 @@ class Comprehension(IterableWrapper): unpack_tuple_to_dict(evaluator, types, exprlist) try: if len(comp_fors) > 1: - for result in nested(types, comp_fors[1:]): + for result in nested(types, comp_fors[1:], exprlist): yield result else: yield evaluator.eval_element(self.eval_node()) @@ -171,7 +171,7 @@ class Comprehension(IterableWrapper): comp_fors = list(self._get_comp_for().get_comp_fors()) input_node = comp_fors[0].children[-1] input_types = evaluator.eval_element(input_node) - for result in nested(input_types, comp_fors): + for result in nested(input_types, comp_fors, input_node): yield result def get_exact_index_types(self, index): @@ -466,26 +466,24 @@ def unpack_tuple_to_dict(evaluator, types, exprlist): raise NotImplementedError -def py__iter__(evaluator, types): +def py__iter__(evaluator, types, node): debug.dbg('py__iter__') for typ in types: try: iter_method = typ.py__iter__ except AttributeError: - raise NotImplementedError - analysis.add(evaluator, 'type-error-not-iterable', element) - debug.warning('iterator/for loop input wrong: %s', it) + analysis.add(evaluator, 'type-error-not-iterable', node) else: for result in iter_method(): yield result -def py__iter__types(evaluator, types): +def py__iter__types(evaluator, types, node): """ Calls `py__iter__`, but ignores the ordering in the end and just returns all types that it contains. """ - return unite(py__iter__(evaluator, types)) + return unite(py__iter__(evaluator, types, node)) def check_array_additions(evaluator, array): @@ -529,8 +527,9 @@ def _check_array_additions(evaluator, compare_array, module, is_list): result |= unite(evaluator.eval_element(node) for node in nodes) elif add_name in ['extend', 'update']: for key, nodes in params: - types = unite(evaluator.eval_element(n) for n in nodes) - result |= py__iter__types(evaluator, types) + for node in nodes: + types = evaluator.eval_element(node) + result |= py__iter__types(evaluator, types, node) return result from jedi.evaluate import representation as er, param @@ -641,8 +640,9 @@ class _ArrayInstance(IterableWrapper): """ items = set() for key, nodes in self.var_args.unpack(): - types = unite(self._evaluator.eval_element(n) for n in nodes) - items |= py__iter__types(self._evaluator, types) + for node in nodes: + types = self._evaluator.eval_element(node) + items |= py__iter__types(self._evaluator, types, node) module = self.var_args.get_parent_until() is_list = str(self.instance.name) == 'list' @@ -657,13 +657,13 @@ class _ArrayInstance(IterableWrapper): else: types = unite(self._evaluator.eval_element(node) for node in first_nodes) - for types in py__iter__(self._evaluator, types): - yield types - module = self.var_args.get_parent_until() - is_list = str(self.instance.name) == 'list' - additions = _check_array_additions(self._evaluator, self.instance, module, is_list) - if additions: - yield additions + for types in py__iter__(self._evaluator, types, first_nodes[0]): + yield types + module = self.var_args.get_parent_until() + is_list = str(self.instance.name) == 'list' + additions = _check_array_additions(self._evaluator, self.instance, module, is_list) + if additions: + yield additions class Slice(object): diff --git a/jedi/evaluate/param.py b/jedi/evaluate/param.py index 5465cb10..e33f2f3e 100644 --- a/jedi/evaluate/param.py +++ b/jedi/evaluate/param.py @@ -16,11 +16,12 @@ def try_iter_content(types): """Helper method for static analysis.""" for typ in types: try: - f = typ.iter_content + f = typ.py__iter__ except AttributeError: pass else: - try_iter_content(f()) + for iter_types in f(): + try_iter_content(iter_types) class Arguments(tree.Base): diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index 1e6b83b1..fe006bb5 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -688,8 +688,9 @@ class FunctionExecution(Executed): for yield_ in yields: yield evaluator.eval_element(yield_.children[1]) else: - for_types = evaluator.eval_element(for_stmt.get_input_node()) - ordered = iterable.py__iter__(evaluator, for_types) + input_node = for_stmt.get_input_node() + for_types = evaluator.eval_element(input_node) + ordered = iterable.py__iter__(evaluator, for_types, input_node) for index_types in ordered: dct = {str(for_stmt.children[1]): index_types} evaluator.predefined_if_name_dict_dict[for_stmt] = dct