Skip to content

Commit 097eae5

Browse files
[3.8] bpo-37950: Fix ast.dump() when call with incompletely initialized node. (GH-15510) (GH-15582)
(cherry picked from commit e64f948)
1 parent 38d311d commit 097eae5

File tree

4 files changed

+59
-19
lines changed

4 files changed

+59
-19
lines changed

Doc/library/ast.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -322,11 +322,12 @@ and classes for traversing abstract syntax trees:
322322
.. function:: dump(node, annotate_fields=True, include_attributes=False)
323323

324324
Return a formatted dump of the tree in *node*. This is mainly useful for
325-
debugging purposes. The returned string will show the names and the values
326-
for fields. This makes the code impossible to evaluate, so if evaluation is
327-
wanted *annotate_fields* must be set to ``False``. Attributes such as line
325+
debugging purposes. If *annotate_fields* is true (by default),
326+
the returned string will show the names and the values for fields.
327+
If *annotate_fields* is false, the result string will be more compact by
328+
omitting unambiguous field names. Attributes such as line
328329
numbers and column offsets are not dumped by default. If this is wanted,
329-
*include_attributes* can be set to ``True``.
330+
*include_attributes* can be set to true.
330331

331332
.. seealso::
332333

Lib/ast.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -98,26 +98,35 @@ def _convert(node):
9898

9999
def dump(node, annotate_fields=True, include_attributes=False):
100100
"""
101-
Return a formatted dump of the tree in *node*. This is mainly useful for
102-
debugging purposes. The returned string will show the names and the values
103-
for fields. This makes the code impossible to evaluate, so if evaluation is
104-
wanted *annotate_fields* must be set to False. Attributes such as line
101+
Return a formatted dump of the tree in node. This is mainly useful for
102+
debugging purposes. If annotate_fields is true (by default),
103+
the returned string will show the names and the values for fields.
104+
If annotate_fields is false, the result string will be more compact by
105+
omitting unambiguous field names. Attributes such as line
105106
numbers and column offsets are not dumped by default. If this is wanted,
106-
*include_attributes* can be set to True.
107+
include_attributes can be set to true.
107108
"""
108109
def _format(node):
109110
if isinstance(node, AST):
110-
fields = [(a, _format(b)) for a, b in iter_fields(node)]
111-
rv = '%s(%s' % (node.__class__.__name__, ', '.join(
112-
('%s=%s' % field for field in fields)
113-
if annotate_fields else
114-
(b for a, b in fields)
115-
))
111+
args = []
112+
keywords = annotate_fields
113+
for field in node._fields:
114+
try:
115+
value = getattr(node, field)
116+
except AttributeError:
117+
keywords = True
118+
else:
119+
if keywords:
120+
args.append('%s=%s' % (field, _format(value)))
121+
else:
122+
args.append(_format(value))
116123
if include_attributes and node._attributes:
117-
rv += fields and ', ' or ' '
118-
rv += ', '.join('%s=%s' % (a, _format(getattr(node, a)))
119-
for a in node._attributes)
120-
return rv + ')'
124+
for a in node._attributes:
125+
try:
126+
args.append('%s=%s' % (a, _format(getattr(node, a))))
127+
except AttributeError:
128+
pass
129+
return '%s(%s)' % (node.__class__.__name__, ', '.join(args))
121130
elif isinstance(node, list):
122131
return '[%s]' % ', '.join(_format(x) for x in node)
123132
return repr(node)

Lib/test/test_ast.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,35 @@ def test_dump(self):
645645
"lineno=1, col_offset=0, end_lineno=1, end_col_offset=24)], type_ignores=[])"
646646
)
647647

648+
def test_dump_incomplete(self):
649+
node = ast.Raise(lineno=3, col_offset=4)
650+
self.assertEqual(ast.dump(node),
651+
"Raise()"
652+
)
653+
self.assertEqual(ast.dump(node, include_attributes=True),
654+
"Raise(lineno=3, col_offset=4)"
655+
)
656+
node = ast.Raise(exc=ast.Name(id='e', ctx=ast.Load()), lineno=3, col_offset=4)
657+
self.assertEqual(ast.dump(node),
658+
"Raise(exc=Name(id='e', ctx=Load()))"
659+
)
660+
self.assertEqual(ast.dump(node, annotate_fields=False),
661+
"Raise(Name('e', Load()))"
662+
)
663+
self.assertEqual(ast.dump(node, include_attributes=True),
664+
"Raise(exc=Name(id='e', ctx=Load()), lineno=3, col_offset=4)"
665+
)
666+
self.assertEqual(ast.dump(node, annotate_fields=False, include_attributes=True),
667+
"Raise(Name('e', Load()), lineno=3, col_offset=4)"
668+
)
669+
node = ast.Raise(cause=ast.Name(id='e', ctx=ast.Load()))
670+
self.assertEqual(ast.dump(node),
671+
"Raise(cause=Name(id='e', ctx=Load()))"
672+
)
673+
self.assertEqual(ast.dump(node, annotate_fields=False),
674+
"Raise(cause=Name('e', Load()))"
675+
)
676+
648677
def test_copy_location(self):
649678
src = ast.parse('1 + 1', mode='eval')
650679
src.body.right = ast.copy_location(ast.Num(2), src.body.right)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix :func:`ast.dump` when call with incompletely initialized node.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy