From 9bd6e6c340fdd704cf57cfd6caa4da32e9a8755b Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Thu, 10 Dec 2015 04:37:23 +0100 Subject: [PATCH] Fix: iterators are working smoothly now. Finally tests are passing again. --- jedi/api/__init__.py | 1 + jedi/evaluate/analysis.py | 19 ++++++------ jedi/evaluate/finder.py | 1 + jedi/evaluate/iterable.py | 49 ++++++++++++++++++------------ jedi/evaluate/param.py | 4 +-- test/completion/generators.py | 2 +- test/static_analysis/generators.py | 2 +- 7 files changed, 45 insertions(+), 33 deletions(-) diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index c5c105fc..018f8284 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -631,6 +631,7 @@ class Interpreter(Script): if isinstance(user_stmt, tree.Import) or not is_simple_path: return super(Interpreter, self)._simple_complete(path, dot, like) else: + # TODO Remove this branch? The above branch should be fast enough IMO. class NamespaceModule(object): def __getattr__(_, name): for n in self.namespaces: diff --git a/jedi/evaluate/analysis.py b/jedi/evaluate/analysis.py index 10f01b8c..1712f8db 100644 --- a/jedi/evaluate/analysis.py +++ b/jedi/evaluate/analysis.py @@ -10,16 +10,15 @@ CODES = { 'attribute-error': (1, AttributeError, 'Potential AttributeError.'), 'name-error': (2, NameError, 'Potential NameError.'), 'import-error': (3, ImportError, 'Potential ImportError.'), - 'type-error-generator': (4, TypeError, "TypeError: 'generator' object is not subscriptable."), - 'type-error-too-many-arguments': (5, TypeError, None), - 'type-error-too-few-arguments': (6, TypeError, None), - 'type-error-keyword-argument': (7, TypeError, None), - 'type-error-multiple-values': (8, TypeError, None), - 'type-error-star-star': (9, TypeError, None), - 'type-error-star': (10, TypeError, None), - 'type-error-operation': (11, TypeError, None), - 'type-error-not-iterable': (12, TypeError, None), - 'type-error-isinstance': (13, TypeError, None), + 'type-error-too-many-arguments': (4, TypeError, None), + 'type-error-too-few-arguments': (5, TypeError, None), + 'type-error-keyword-argument': (6, TypeError, None), + 'type-error-multiple-values': (7, TypeError, None), + 'type-error-star-star': (8, TypeError, None), + 'type-error-star': (9, TypeError, None), + 'type-error-operation': (10, TypeError, None), + 'type-error-not-iterable': (11, TypeError, None), + 'type-error-isinstance': (12, TypeError, None), 'type-error-not-subscriptable': (13, TypeError, None), } diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 9e25ad6d..e81c081b 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -250,6 +250,7 @@ class NameFinder(object): def _names_to_types(self, names, search_global): types = set() + debug.warning('start nt %s', names) # Add isinstance and other if/assert knowledge. if isinstance(self.name_str, tree.Name): # Ignore FunctionExecution parents for now. diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index cebe120d..7a02d39a 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -23,7 +23,7 @@ It is important to note that: from jedi.common import unite, ignored, safe_property from jedi import debug from jedi import settings -from jedi._compatibility import use_metaclass, unicode +from jedi._compatibility import use_metaclass, unicode, zip_longest from jedi.parser import tree from jedi.evaluate import compiled from jedi.evaluate import helpers @@ -51,14 +51,12 @@ class GeneratorMixin(object): dct[name.value] = [name] yield dct - def get_index_types(self, evaluator, index_array): - #debug.warning('Tried to get array access on a generator: %s', self) - analysis.add(self._evaluator, 'type-error-generator', index_array) - return set() - def py__bool__(self): return True + def get_index_types(self): + raise NotImplementedError + def py__class__(self, evaluator): gen_obj = compiled.get_special_object(self._evaluator, 'GENERATOR_OBJECT') return gen_obj.py__class__(evaluator) @@ -76,6 +74,7 @@ class Generator(use_metaclass(CachedMetaClass, IterableWrapper, GeneratorMixin)) """ returns the content of __iter__ """ # Directly execute it, because with a normal call to py__call__ a # Generator will be returned. + raise NotImplementedError from jedi.evaluate.representation import FunctionExecution f = FunctionExecution(self._evaluator, self.func, self.var_args) return f.get_return_types(check_yields=True) @@ -90,6 +89,7 @@ class Generator(use_metaclass(CachedMetaClass, IterableWrapper, GeneratorMixin)) Exact lookups are used for tuple lookups, which are perfectly fine if used with generators. """ + raise NotImplementedError return list(self.py__iter__())[index] def __getattr__(self, name): @@ -112,7 +112,7 @@ class GeneratorMethod(IterableWrapper): def py__call__(self, evaluator, params): # TODO add TypeError if params are given. - return self._generator.iter_content() + return unite(self._generator.py__iter__()) def __getattr__(self, name): return getattr(self._builtin_func, name) @@ -152,27 +152,27 @@ 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, node): - iterated = py__iter__(evaluator, input_types, node) + def nested(comp_fors): comp_for = comp_fors[0] + input_node = comp_for.children[3] + input_types = evaluator.eval_element(input_node) + + iterated = py__iter__(evaluator, input_types, input_node) exprlist = comp_for.children[1] for types in iterated: evaluator.predefined_if_name_dict_dict[comp_for] = \ unpack_tuple_to_dict(evaluator, types, exprlist) try: - if len(comp_fors) > 1: - for result in nested(types, comp_fors[1:], exprlist): - yield result - else: - yield evaluator.eval_element(self.eval_node()) + for result in nested(comp_fors[1:]): + yield result + except IndexError: + yield evaluator.eval_element(self.eval_node()) finally: del evaluator.predefined_if_name_dict_dict[comp_for] evaluator = self._evaluator comp_fors = list(self._get_comp_for().get_comp_fors()) - input_node = comp_fors[0].children[3] - input_types = evaluator.eval_element(input_node) - for result in nested(input_types, comp_fors, input_node): + for result in nested(comp_fors): yield result def get_exact_index_types(self, index): @@ -203,9 +203,11 @@ class ListComprehension(Comprehension, ArrayMixin): type = 'list' def get_index_types(self, evaluator, index): + raise NotImplementedError return self.iter_content() def iter_content(self): + raise NotImplementedError return self._evaluator.eval_element(self.eval_node()) def py__getitem__(self, index): @@ -219,6 +221,7 @@ class ListComprehension(Comprehension, ArrayMixin): class GeneratorComprehension(Comprehension, GeneratorMixin): def iter_content(self): + raise NotImplementedError return self._evaluator.eval_element(self.eval_node()) @@ -251,6 +254,7 @@ class Array(IterableWrapper, ArrayMixin): :param index: A subscriptlist node (or subnode). """ + raise NotImplementedError indexes = create_index_types(evaluator, index) lookup_done = False types = set() @@ -296,6 +300,7 @@ class Array(IterableWrapper, ArrayMixin): return self._evaluator.eval_element(self._items()[index]) def iter_content(self): + raise NotImplementedError return self.values() @safe_property @@ -489,6 +494,7 @@ def unpack_tuple_to_dict(evaluator, types, exprlist): def py__iter__(evaluator, types, node=None): debug.dbg('py__iter__') + type_iters = [] for typ in types: try: iter_method = typ.py__iter__ @@ -497,8 +503,12 @@ def py__iter__(evaluator, types, node=None): analysis.add(evaluator, 'type-error-not-iterable', node, message="TypeError: '%s' object is not iterable" % typ) else: - for result in iter_method(): - yield result + type_iters.append(iter_method()) + #for result in iter_method(): + #yield result + + for t in zip_longest(*type_iters, fillvalue=set()): + yield unite(t) def py__iter__types(evaluator, types, node=None): @@ -696,6 +706,7 @@ class _ArrayInstance(IterableWrapper): The index is here just ignored, because of all the appends, etc. lists/sets are too complicated too handle that. """ + raise NotImplementedError items = set() for key, nodes in self.var_args.unpack(): for node in nodes: diff --git a/jedi/evaluate/param.py b/jedi/evaluate/param.py index feef396d..fb9dac68 100644 --- a/jedi/evaluate/param.py +++ b/jedi/evaluate/param.py @@ -383,8 +383,8 @@ def _iterate_star_args(evaluator, array, input_node, func=None): for field_stmt in array: # yield from plz! yield field_stmt elif isinstance(array, iterable.Generator): - for field_stmt in array.iter_content(): - yield iterable.AlreadyEvaluated([field_stmt]) + for types in array.py__iter__(): + yield iterable.AlreadyEvaluated(types) elif isinstance(array, Instance) and array.name.get_code() == 'tuple': debug.warning('Ignored a tuple *args input %s' % array) else: diff --git a/test/completion/generators.py b/test/completion/generators.py index 5a01d880..07c4b72f 100644 --- a/test/completion/generators.py +++ b/test/completion/generators.py @@ -120,7 +120,7 @@ def gen(): a, b = next(gen()) #? int() a -#? str() +#? str() float() b diff --git a/test/static_analysis/generators.py b/test/static_analysis/generators.py index 6da66079..b9418002 100644 --- a/test/static_analysis/generators.py +++ b/test/static_analysis/generators.py @@ -1,7 +1,7 @@ def generator(): yield 1 -#! 12 type-error-generator +#! 11 type-error-not-subscriptable generator()[0] list(generator())[0]