diff options
-rw-r--r-- | ChangeLog | 3 | ||||
-rw-r--r-- | as_string.py | 9 | ||||
-rw-r--r-- | node_classes.py | 3 | ||||
-rw-r--r-- | nodes.py | 4 | ||||
-rw-r--r-- | rebuilder.py | 18 | ||||
-rw-r--r-- | scoped_nodes.py | 5 | ||||
-rw-r--r-- | test/unittest_python3.py | 42 |
7 files changed, 69 insertions, 15 deletions
@@ -14,7 +14,8 @@ Change log for the astroid package (used to be astng) * Don't crash when inferring nodes from `with` clauses with multiple context managers. Closes #18. - + + * Add a new YieldFrom node. 2013-10-18 -- 1.0.1 * fix py3k/windows installation issue (issue #4) diff --git a/as_string.py b/as_string.py index 72c5b6c3..ace1c4e3 100644 --- a/as_string.py +++ b/as_string.py @@ -467,6 +467,15 @@ class AsStringVisitor3k(AsStringVisitor): """return Starred node as string""" return "*" + node.value.accept(self) + def visit_yieldfrom(self, node): + """ Return an astroid.YieldFrom node as string. """ + yi_val = node.value and (" " + node.value.accept(self)) or "" + expr = 'yield from' + yi_val + if node.parent.is_statement: + return expr + else: + return "(%s)" % (expr,) + def _import_string(names): """return a list of (name, asname) formatted as a string""" diff --git a/node_classes.py b/node_classes.py index 97bd9079..01dc8d92 100644 --- a/node_classes.py +++ b/node_classes.py @@ -888,6 +888,9 @@ class Yield(NodeNG): _astroid_fields = ('value',) value = None +class YieldFrom(Yield): + """ Class representing a YieldFrom node. """ + # constants ############################################################## CONST_CLS = { @@ -43,7 +43,7 @@ from astroid.node_classes import Arguments, AssAttr, Assert, Assign, \ Dict, Discard, Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, For, \ From, Getattr, Global, If, IfExp, Import, Index, Keyword, \ List, Name, Nonlocal, Pass, Print, Raise, Return, Set, Slice, Starred, Subscript, \ - TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield, \ + TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield, YieldFrom, \ const_factory from astroid.scoped_nodes import Module, GenExpr, Lambda, DictComp, \ ListComp, SetComp, Function, Class @@ -68,6 +68,6 @@ ALL_NODE_CLASSES = ( TryExcept, TryFinally, Tuple, UnaryOp, While, With, - Yield, + Yield, YieldFrom ) diff --git a/rebuilder.py b/rebuilder.py index b9a9adbb..f6fae7b1 100644 --- a/rebuilder.py +++ b/rebuilder.py @@ -122,6 +122,14 @@ def _infer_metaclass(node): elif isinstance(node, Attribute): return node.attr +def _create_yield_node(node, parent, rebuilder, factory): + newnode = factory() + _lineno_parent(node, newnode, parent) + if node.value is not None: + newnode.value = rebuilder.visit(node.value, newnode) + newnode.set_line_info(newnode.last_child()) + return newnode + class TreeRebuilder(object): """Rebuilds the _ast tree to become an Astroid tree""" @@ -837,13 +845,7 @@ class TreeRebuilder(object): def visit_yield(self, node, parent): """visit a Yield node by returning a fresh instance of it""" - newnode = new.Yield() - _lineno_parent(node, newnode, parent) - if node.value is not None: - newnode.value = self.visit(node.value, newnode) - newnode.set_line_info(newnode.last_child()) - return newnode - + return _create_yield_node(node, parent, self, new.Yield) class TreeRebuilder3k(TreeRebuilder): """extend and overwrite TreeRebuilder for python3k""" @@ -954,7 +956,7 @@ class TreeRebuilder3k(TreeRebuilder): return newnode def visit_yieldfrom(self, node, parent): - return self.visit_yield(node, parent) + return _create_yield_node(node, parent, self, new.YieldFrom) def visit_class(self, node, parent): newnode = super(TreeRebuilder3k, self).visit_class(node, parent) diff --git a/scoped_nodes.py b/scoped_nodes.py index a7f6ee8c..d579913a 100644 --- a/scoped_nodes.py +++ b/scoped_nodes.py @@ -32,7 +32,7 @@ from logilab.common.decorators import cached from astroid.exceptions import NotFoundError, \ AstroidBuildingException, InferenceError from astroid.node_classes import Const, DelName, DelAttr, \ - Dict, From, List, Pass, Raise, Return, Tuple, Yield, \ + Dict, From, List, Pass, Raise, Return, Tuple, Yield, YieldFrom, \ LookupMixIn, const_factory as cf, unpack_infer from astroid.bases import NodeNG, InferenceContext, Instance,\ YES, Generator, UnboundMethod, BoundMethod, _infer_stmts, copy_context, \ @@ -620,7 +620,8 @@ class Function(Statement, Lambda): """return true if this is a generator function""" # XXX should be flagged, not computed try: - return self.nodes_of_class(Yield, skip_klass=(Function, Lambda)).next() + return self.nodes_of_class((Yield, YieldFrom), + skip_klass=(Function, Lambda)).next() except StopIteration: return False diff --git a/test/unittest_python3.py b/test/unittest_python3.py index d17b1432..96de6327 100644 --- a/test/unittest_python3.py +++ b/test/unittest_python3.py @@ -20,10 +20,10 @@ from textwrap import dedent from logilab.common.testlib import TestCase, unittest_main, require_version -from astroid.node_classes import Assign +from astroid.node_classes import Assign, Discard, YieldFrom from astroid.manager import AstroidManager from astroid.builder import AstroidBuilder -from astroid.scoped_nodes import Class +from astroid.scoped_nodes import Class, Function class Python3TC(TestCase): @@ -41,6 +41,44 @@ class Python3TC(TestCase): self.assertTrue(isinstance(node.ass_type(), Assign)) + @require_version('3.3') + def test_yield_from(self): + body = dedent(""" + def func(): + yield from iter([1, 2]) + """) + astroid = self.builder.string_build(body) + func = astroid.body[0] + self.assertIsInstance(func, Function) + yieldfrom_stmt = func.body[0] + + self.assertIsInstance(yieldfrom_stmt, Discard) + self.assertIsInstance(yieldfrom_stmt.value, YieldFrom) + self.assertEqual(yieldfrom_stmt.as_string(), + 'yield from iter([1, 2])') + + @require_version('3.3') + def test_yield_from_is_generator(self): + body = dedent(""" + def func(): + yield from iter([1, 2]) + """) + astroid = self.builder.string_build(body) + func = astroid.body[0] + self.assertIsInstance(func, Function) + self.assertTrue(func.is_generator()) + + @require_version('3.3') + def test_yield_from_as_string(self): + body = dedent(""" + def func(): + yield from iter([1, 2]) + value = yield from other() + """) + astroid = self.builder.string_build(body) + func = astroid.body[0] + self.assertEqual(func.as_string().strip(), body.strip()) + # metaclass tests @require_version('3.0') |