diff options
author | Claudiu Popa <pcmanticore@gmail.com> | 2019-06-01 21:47:49 +0200 |
---|---|---|
committer | Claudiu Popa <pcmanticore@gmail.com> | 2019-06-01 21:47:49 +0200 |
commit | a63f18dfa7e15f8243409bd121011b7c01707582 (patch) | |
tree | fc7538300fb21cdcb4abe9d73e51c8acbe6c3cb1 | |
parent | b58d4628b7b40e0af882166ffe945f7cb9d2dbda (diff) | |
download | astroid-git-a63f18dfa7e15f8243409bd121011b7c01707582.tar.gz |
Add support for Python 3.8's `NamedExpr` nodes, which is part of assignment expressions.
Close #674
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | astroid/as_string.py | 6 | ||||
-rw-r--r-- | astroid/node_classes.py | 25 | ||||
-rw-r--r-- | astroid/nodes.py | 2 | ||||
-rw-r--r-- | astroid/rebuilder.py | 7 | ||||
-rw-r--r-- | astroid/tests/unittest_nodes.py | 25 |
6 files changed, 69 insertions, 0 deletions
@@ -6,6 +6,10 @@ What's New in astroid 2.3.0? ============================ Release Date: TBA +* Add support for Python 3.8's `NamedExpr` nodes, which is part of assignment expressions. + + Close #674 + * Added support for inferring `IfExp` nodes. * Instances of exceptions are inferred as such when inferring in non-exception context diff --git a/astroid/as_string.py b/astroid/as_string.py index 7042272f..9d92b3cf 100644 --- a/astroid/as_string.py +++ b/astroid/as_string.py @@ -611,6 +611,12 @@ class AsStringVisitor3(AsStringVisitor): super(AsStringVisitor3, self).visit_comprehension(node), ) + def visit_namedexpr(self, node): + """Return an assignment expression node as string""" + target = node.target.accept(self) + value = node.value.accept(self) + return "%s := %s" % (target, value) + def _import_string(names): """return a list of (name, asname) formatted as a string""" diff --git a/astroid/node_classes.py b/astroid/node_classes.py index 2408e568..d3d7634d 100644 --- a/astroid/node_classes.py +++ b/astroid/node_classes.py @@ -4623,6 +4623,31 @@ class JoinedStr(NodeNG): yield from self.values +class NamedExpr(mixins.AssignTypeMixin, NodeNG): + """Represents the assignment from the assignment expression + + >>> module = astroid.parse('if a := 1: pass') + >>> module.body[0].test + <NamedExpr l.1 at 0x7f23b2e4ed30> + """ + + _astroid_fields = ("target", "value") + target = None + """The assignment target + + :type: Name + """ + value = None + """The value that gets assigned in the expression + + :type: NodeNG + """ + + def postinit(self, target, value): + self.target = target + self.value = value + + class Unknown(mixins.AssignTypeMixin, NodeNG): """This node represents a node in a constructed AST where introspection is not possible. At the moment, it's only used in diff --git a/astroid/nodes.py b/astroid/nodes.py index 20672593..bf6911ac 100644 --- a/astroid/nodes.py +++ b/astroid/nodes.py @@ -59,6 +59,7 @@ from astroid.node_classes import ( Keyword, List, Name, + NamedExpr, Nonlocal, Pass, Print, @@ -149,6 +150,7 @@ ALL_NODE_CLASSES = ( List, ListComp, Name, + NamedExpr, Nonlocal, Module, Pass, diff --git a/astroid/rebuilder.py b/astroid/rebuilder.py index c18893d8..a60785c4 100644 --- a/astroid/rebuilder.py +++ b/astroid/rebuilder.py @@ -1063,6 +1063,13 @@ class TreeRebuilder3(TreeRebuilder): ) return newnode + def visit_namedexpr(self, node, parent): + newnode = nodes.NamedExpr(node.lineno, node.col_offset, parent) + newnode.postinit( + self.visit(node.target, newnode), self.visit(node.value, newnode) + ) + return newnode + if sys.version_info >= (3, 0): TreeRebuilder = TreeRebuilder3 diff --git a/astroid/tests/unittest_nodes.py b/astroid/tests/unittest_nodes.py index 800eef3a..96a7f830 100644 --- a/astroid/tests/unittest_nodes.py +++ b/astroid/tests/unittest_nodes.py @@ -1134,5 +1134,30 @@ def test_f_string_correct_line_numbering(): assert node.last_child().last_child().lineno == 5 +@pytest.mark.skipif( + sys.version_info[:2] < (3, 8), reason="needs assignment expressions" +) +def test_assignment_expression(): + code = """ + if __(a := 1): + pass + if __(b := test): + pass + """ + first, second = astroid.extract_node(code) + + assert isinstance(first.target, nodes.AssignName) + assert first.target.name == "a" + assert isinstance(first.value, nodes.Const) + assert first.value.value == 1 + assert first.as_string() == "a := 1" + + assert isinstance(second.target, nodes.AssignName) + assert second.target.name == "b" + assert isinstance(second.value, nodes.Name) + assert second.value.name == "test" + assert second.as_string() == "b := test" + + if __name__ == "__main__": unittest.main() |