diff --git a/jedi/evaluate/compiled/__init__.py b/jedi/evaluate/compiled/__init__.py index f8af842b..88c5706f 100644 --- a/jedi/evaluate/compiled/__init__.py +++ b/jedi/evaluate/compiled/__init__.py @@ -198,6 +198,17 @@ class CompiledObject(Base): pass # self.obj maynot have an __iter__ method. return result + 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 + + for obj in self.obj: + yield set([CompiledObject(obj)]) + @property def name(self): # might not exist sometimes (raises AttributeError) diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index ddd53ce0..bd63bae3 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -298,28 +298,27 @@ class NameFinder(object): @memoize_default(set(), evaluator_is_first_arg=True) def _name_to_types(evaluator, name, scope): - types = set() typ = name.get_definition() if typ.isinstance(tree.ForStmt, tree.CompFor): container_types = evaluator.eval_element(typ.children[3]) for_types = common.unite(iterable.py__iter__(evaluator, container_types)) - types |= check_tuple_assignments(for_types, name) + types = check_tuple_assignments(for_types, name) elif isinstance(typ, tree.Param): - types |= _eval_param(evaluator, typ, scope) + types = _eval_param(evaluator, typ, scope) elif typ.isinstance(tree.ExprStmt): - types |= _remove_statements(evaluator, typ, name) + types = _remove_statements(evaluator, typ, name) elif typ.isinstance(tree.WithStmt): - types |= evaluator.eval_element(typ.node_from_name(name)) + types = evaluator.eval_element(typ.node_from_name(name)) elif isinstance(typ, tree.Import): - types |= imports.ImportWrapper(evaluator, name).follow() + types = imports.ImportWrapper(evaluator, name).follow() elif isinstance(typ, tree.GlobalStmt): # TODO theoretically we shouldn't be using search_global here, it # doesn't make sense, because it's a local search (for that name)! # However, globals are not that important and resolving them doesn't # guarantee correctness in any way, because we don't check for when # something is executed. - types |= evaluator.find_types(typ.get_parent_scope(), str(name), - search_global=True) + types = evaluator.find_types(typ.get_parent_scope(), str(name), + search_global=True) elif isinstance(typ, tree.TryStmt): # TODO an exception can also be a tuple. Check for those. # TODO check for types that are not classes and add it to @@ -329,7 +328,7 @@ def _name_to_types(evaluator, name, scope): else: if typ.isinstance(er.Function): typ = typ.get_decorated_func() - types.add(typ) + types = set([typ]) return types diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index 6000c17a..3cbf9bb4 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -316,6 +316,8 @@ class Array(IterableWrapper, ArrayMixin): for value in iterate: yield self._evaluator.eval_element(value) + yield check_array_additions(self._evaluator, self) + def _values(self): """Returns a list of a list of node.""" if self.type == 'dict': @@ -485,9 +487,15 @@ def unpack_tuple_to_dict(evaluator, types, exprlist): def py__iter__(evaluator, types): + debug.dbg('py__iter__') for typ in types: - for result in typ.py__iter__(): - yield result + try: + iter_method = typ.py__iter__ + except AttributeError: + raise NotImplementedError + else: + for result in iter_method(): + yield result def get_iterator_types(evaluator, element): @@ -690,15 +698,18 @@ class _ArrayInstance(IterableWrapper): return items def py__iter__(self): - _, first_nodes = next(self.var_args.unpack()) - types = unite(self._evaluator.eval_element(node) for node in first_nodes) + try: + _, first_nodes = next(self.var_args.unpack()) + except StopIteration: + types = set() + else: + types = unite(self._evaluator.eval_element(node) for node in first_nodes) - for typ in py__iter__(self._evaluator, types): - yield typ + for types in py__iter__(self._evaluator, types): + yield types module = self.var_args.get_parent_until() is_list = str(self.instance.name) == 'list' - for typ in _check_array_additions(self._evaluator, self.instance, module, is_list): - yield typ + yield _check_array_additions(self._evaluator, self.instance, module, is_list) class Slice(object):