summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2019-08-29 06:30:23 (GMT)
committerGitHub <noreply@github.com>2019-08-29 06:30:23 (GMT)
commite64f948e762a6b9fd02e2902ccf42438df6fcb61 (patch)
tree833c88bf7abd2b0ded10bd61468c0d238dedf514
parentb235a1b47394eedc5f8ea4cf214f56c4c6932e59 (diff)
downloadcpython-e64f948e762a6b9fd02e2902ccf42438df6fcb61.zip
cpython-e64f948e762a6b9fd02e2902ccf42438df6fcb61.tar.gz
cpython-e64f948e762a6b9fd02e2902ccf42438df6fcb61.tar.bz2
bpo-37950: Fix ast.dump() when call with incompletely initialized node. (GH-15510)
-rw-r--r--Doc/library/ast.rst9
-rw-r--r--Lib/ast.py39
-rw-r--r--Lib/test/test_ast.py29
-rw-r--r--Misc/NEWS.d/next/Library/2019-08-26-10-45-51.bpo-37950.-K1IKT.rst1
4 files changed, 59 insertions, 19 deletions
diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst
index 3d2c420..92bf891 100644
--- a/Doc/library/ast.rst
+++ b/Doc/library/ast.rst
@@ -322,11 +322,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 1e639d1..5ab023f 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -98,26 +98,35 @@ def literal_eval(node_or_string):
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 3d12397..07bbb4c 100644
--- a/Lib/test/test_ast.py
+++ b/Lib/test/test_ast.py
@@ -645,6 +645,35 @@ class ASTHelpers_Test(unittest.TestCase):
"lineno=1, col_offset=0, end_lineno=1, end_col_offset=24)], type_ignores=[])"
)
+ 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 0000000..ded80d3
--- /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.