[Python-checkins] bpo-37950: Fix ast.dump() when call with incompletely initialized node. (GH-15510)

Miss Islington (bot) webhook-mailer at python.org
Thu Aug 29 03:04:48 EDT 2019


https://github.com/python/cpython/commit/d3d2650cf84e6be0f84d6d0d538999d1e0cfdd43
commit: d3d2650cf84e6be0f84d6d0d538999d1e0cfdd43
branch: 3.7
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2019-08-29T00:04:44-07:00
summary:

bpo-37950: Fix ast.dump() when call with incompletely initialized node. (GH-15510)

(cherry picked from commit e64f948e762a6b9fd02e2902ccf42438df6fcb61)

Co-authored-by: Serhiy Storchaka <storchaka at gmail.com>

files:
A Misc/NEWS.d/next/Library/2019-08-26-10-45-51.bpo-37950.-K1IKT.rst
M Doc/library/ast.rst
M Lib/ast.py
M Lib/test/test_ast.py

diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst
index 3c73f748acbe..699ad0456a02 100644
--- a/Doc/library/ast.rst
+++ b/Doc/library/ast.rst
@@ -262,11 +262,12 @@ and classes for traversing abstract syntax trees:
 .. function:: dump(node, annotate_fields=True, include_attributes=False)
 
    Return a formatted dump of the tree in *node*.  This is mainly useful for
-   debugging purposes.  The returned string will show the names and the values
-   for fields.  This makes the code impossible to evaluate, so if evaluation is
-   wanted *annotate_fields* must be set to ``False``.  Attributes such as line
+   debugging purposes.  If *annotate_fields* is true (by default),
+   the returned string will show the names and the values for fields.
+   If *annotate_fields* is false, the result string will be more compact by
+   omitting unambiguous field names.  Attributes such as line
    numbers and column offsets are not dumped by default.  If this is wanted,
-   *include_attributes* can be set to ``True``.
+   *include_attributes* can be set to true.
 
 .. seealso::
 
diff --git a/Lib/ast.py b/Lib/ast.py
index bfe346bba8e3..1bc6b38da8d5 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -93,26 +93,35 @@ def _convert(node):
 
 def dump(node, annotate_fields=True, include_attributes=False):
     """
-    Return a formatted dump of the tree in *node*.  This is mainly useful for
-    debugging purposes.  The returned string will show the names and the values
-    for fields.  This makes the code impossible to evaluate, so if evaluation is
-    wanted *annotate_fields* must be set to False.  Attributes such as line
+    Return a formatted dump of the tree in node.  This is mainly useful for
+    debugging purposes.  If annotate_fields is true (by default),
+    the returned string will show the names and the values for fields.
+    If annotate_fields is false, the result string will be more compact by
+    omitting unambiguous field names.  Attributes such as line
     numbers and column offsets are not dumped by default.  If this is wanted,
-    *include_attributes* can be set to True.
+    include_attributes can be set to true.
     """
     def _format(node):
         if isinstance(node, AST):
-            fields = [(a, _format(b)) for a, b in iter_fields(node)]
-            rv = '%s(%s' % (node.__class__.__name__, ', '.join(
-                ('%s=%s' % field for field in fields)
-                if annotate_fields else
-                (b for a, b in fields)
-            ))
+            args = []
+            keywords = annotate_fields
+            for field in node._fields:
+                try:
+                    value = getattr(node, field)
+                except AttributeError:
+                    keywords = True
+                else:
+                    if keywords:
+                        args.append('%s=%s' % (field, _format(value)))
+                    else:
+                        args.append(_format(value))
             if include_attributes and node._attributes:
-                rv += fields and ', ' or ' '
-                rv += ', '.join('%s=%s' % (a, _format(getattr(node, a)))
-                                for a in node._attributes)
-            return rv + ')'
+                for a in node._attributes:
+                    try:
+                        args.append('%s=%s' % (a, _format(getattr(node, a))))
+                    except AttributeError:
+                        pass
+            return '%s(%s)' % (node.__class__.__name__, ', '.join(args))
         elif isinstance(node, list):
             return '[%s]' % ', '.join(_format(x) for x in node)
         return repr(node)
diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py
index 7614d40e3e5c..2d0b5b24ee7d 100644
--- a/Lib/test/test_ast.py
+++ b/Lib/test/test_ast.py
@@ -462,6 +462,35 @@ def test_dump(self):
             "lineno=1, col_offset=0), lineno=1, col_offset=0)])"
         )
 
+    def test_dump_incomplete(self):
+        node = ast.Raise(lineno=3, col_offset=4)
+        self.assertEqual(ast.dump(node),
+            "Raise()"
+        )
+        self.assertEqual(ast.dump(node, include_attributes=True),
+            "Raise(lineno=3, col_offset=4)"
+        )
+        node = ast.Raise(exc=ast.Name(id='e', ctx=ast.Load()), lineno=3, col_offset=4)
+        self.assertEqual(ast.dump(node),
+            "Raise(exc=Name(id='e', ctx=Load()))"
+        )
+        self.assertEqual(ast.dump(node, annotate_fields=False),
+            "Raise(Name('e', Load()))"
+        )
+        self.assertEqual(ast.dump(node, include_attributes=True),
+            "Raise(exc=Name(id='e', ctx=Load()), lineno=3, col_offset=4)"
+        )
+        self.assertEqual(ast.dump(node, annotate_fields=False, include_attributes=True),
+            "Raise(Name('e', Load()), lineno=3, col_offset=4)"
+        )
+        node = ast.Raise(cause=ast.Name(id='e', ctx=ast.Load()))
+        self.assertEqual(ast.dump(node),
+            "Raise(cause=Name(id='e', ctx=Load()))"
+        )
+        self.assertEqual(ast.dump(node, annotate_fields=False),
+            "Raise(cause=Name('e', Load()))"
+        )
+
     def test_copy_location(self):
         src = ast.parse('1 + 1', mode='eval')
         src.body.right = ast.copy_location(ast.Num(2), src.body.right)
diff --git a/Misc/NEWS.d/next/Library/2019-08-26-10-45-51.bpo-37950.-K1IKT.rst b/Misc/NEWS.d/next/Library/2019-08-26-10-45-51.bpo-37950.-K1IKT.rst
new file mode 100644
index 000000000000..ded80d3b00ef
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-08-26-10-45-51.bpo-37950.-K1IKT.rst
@@ -0,0 +1 @@
+Fix :func:`ast.dump` when call with incompletely initialized node.



More information about the Python-checkins mailing list