summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudiu Popa <pcmanticore@gmail.com>2019-06-01 21:47:49 +0200
committerClaudiu Popa <pcmanticore@gmail.com>2019-06-01 21:47:49 +0200
commita63f18dfa7e15f8243409bd121011b7c01707582 (patch)
treefc7538300fb21cdcb4abe9d73e51c8acbe6c3cb1
parentb58d4628b7b40e0af882166ffe945f7cb9d2dbda (diff)
downloadastroid-git-a63f18dfa7e15f8243409bd121011b7c01707582.tar.gz
Add support for Python 3.8's `NamedExpr` nodes, which is part of assignment expressions.
Close #674
-rw-r--r--ChangeLog4
-rw-r--r--astroid/as_string.py6
-rw-r--r--astroid/node_classes.py25
-rw-r--r--astroid/nodes.py2
-rw-r--r--astroid/rebuilder.py7
-rw-r--r--astroid/tests/unittest_nodes.py25
6 files changed, 69 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index 8061d6b6..251794d4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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()