From 9f82cce3bbd203abe2ac01db87ae33f6482e4531 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Mon, 9 Nov 2015 15:15:03 +0100 Subject: [PATCH] Implement py__iter__ for Generators, which means that yield expressions are now orderable, if they are not too complicated. --- jedi/evaluate/iterable.py | 12 ++++++-- jedi/evaluate/representation.py | 50 +++++++++++++++++++++++++++++++-- jedi/parser/tree.py | 2 +- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index 0046f4ce..3108187c 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -86,6 +86,11 @@ class Generator(use_metaclass(CachedMetaClass, IterableWrapper, GeneratorMixin)) f = FunctionExecution(self._evaluator, self.func, self.var_args) return f.get_return_types(check_yields=True) + def py__iter__(self): + from jedi.evaluate.representation import FunctionExecution + f = FunctionExecution(self._evaluator, self.func, self.var_args) + return f.get_yield_types() + def __getattr__(self, name): if name not in ['start_pos', 'end_pos', 'parent', 'get_imports', 'doc', 'docstr', 'get_parent_until', @@ -274,7 +279,7 @@ class Array(IterableWrapper, ArrayMixin): raise AttributeError('Strange access on %s: %s.' % (self, name)) return getattr(self.atom, name) - def per_index_values(self): + def py__iter__(self): """ While values returns the possible values for any array field, this function returns the value for a certain index. @@ -401,12 +406,13 @@ def ordered_elements_of_iterable(evaluator, iterable_type, all_values): # Unpack the iterator values for sequence in iterable_type: try: - per_index_values = sequence.per_index_values + # TODO every type should have a py__iter__ method. + py__iter__ = sequence.py__iter__ except AttributeError: ordered = [literals_to_types(evaluator, all_values)] break else: - for i, types in enumerate(per_index_values()): + for i, types in enumerate(py__iter__()): try: ordered[i] |= types except IndexError: diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index 006f6a59..bc493c90 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -211,6 +211,17 @@ class Instance(use_metaclass(CachedMetaClass, Executed)): else: return self._evaluator.execute(method, [iterable.AlreadyEvaluated(indexes)]) + def py__iter__(self): + try: + method = self.get_subscope_by_name('__iter__') + except KeyError: + debug.warning('No __iter__ on %s.' % self) + return + else: + for generator in self._evaluator.execute(method): + for typ in generator.py__iter__(): + yield typ + @property @underscore_memoization def name(self): @@ -228,8 +239,8 @@ class Instance(use_metaclass(CachedMetaClass, Executed)): dec = '' if self.decorates is not None: dec = " decorates " + repr(self.decorates) - return "" % (type(self).__name__, self.base, - self.var_args, dec) + return "<%s of %s(%s)%s>" % (type(self).__name__, self.base, + self.var_args, dec) class LazyInstanceDict(object): @@ -633,6 +644,41 @@ class FunctionExecution(Executed): break return types + def get_yield_types(self): + yields = self.yields + stopAt = tree.ForStmt, tree.WhileStmt, FunctionExecution + for_parents = [(x, x.get_parent_until((stopAt))) for x in yields] + + # Calculate if the yields are placed within the same for loop. + yields_order = [] + last_for_stmt = None + for yield_, for_stmt in for_parents: + # For really simple for loops we can predict the order. Otherwise + # we just ignore it. + parent = for_stmt.parent + if parent.type == 'suite': + parent = parent.parent + if for_stmt.type == 'for_stmt' and parent == self \ + and for_stmt.defines_one_name(): # Simplicity for now. + if for_stmt == last_for_stmt: + yields_order[-1][1].append(yield_) + else: + yields_order.append((for_stmt, [yield_])) + else: + yield self.get_return_types() + return + last_for_stmt = for_stmt + + for for_stmt, yields in yields_order: + for_types = self._evaluator.eval_element(for_stmt.get_input_node()) + ordered = iterable.ordered_elements_of_iterable(self._evaluator, for_types, []) + for index_types in ordered: + dct = {str(for_stmt.children[1]): index_types} + self._evaluator.predefined_if_name_dict_dict[for_stmt] = dct + for yield_in_same_for_stmt in yields: + yield self._evaluator.eval_element(yield_in_same_for_stmt.children[1]) + del self._evaluator.predefined_if_name_dict_dict[for_stmt] + def names_dicts(self, search_global): yield self.names_dict diff --git a/jedi/parser/tree.py b/jedi/parser/tree.py index 55d995b2..17d33372 100644 --- a/jedi/parser/tree.py +++ b/jedi/parser/tree.py @@ -483,7 +483,7 @@ class BaseNode(Base): @utf8_repr def __repr__(self): - code = self.get_code().replace('\n', ' ') + code = self.get_code().replace('\n', ' ').strip() if not is_py3: code = code.encode(encoding, 'replace') return "<%s: %s@%s,%s>" % \