[Python-checkins] bpo-36287: Make ast.dump() not output optional fields and attributes with default values. (GH-18843)

Serhiy Storchaka webhook-mailer at python.org
Mon Mar 9 18:07:55 EDT 2020


https://github.com/python/cpython/commit/b7e9525f9c7ef02a1d2ad8253afdeb733b0951d4
commit: b7e9525f9c7ef02a1d2ad8253afdeb733b0951d4
branch: master
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2020-03-10T00:07:47+02:00
summary:

bpo-36287: Make ast.dump() not output optional fields and attributes with default values. (GH-18843)

The default values for optional fields and attributes of AST nodes are now set
as class attributes (e.g. Constant.kind is set to None).

files:
A Misc/NEWS.d/next/Library/2020-03-08-09-53-55.bpo-36287.mxr5m8.rst
M Doc/library/ast.rst
M Lib/ast.py
M Lib/test/test_ast.py
M Parser/asdl_c.py
M Python/Python-ast.c

diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst
index 01735643dbb9b..a11f8b9894076 100644
--- a/Doc/library/ast.rst
+++ b/Doc/library/ast.rst
@@ -141,7 +141,7 @@ Literals
 
         >>> print(ast.dump(ast.parse('123', mode='eval'), indent=4))
         Expression(
-            body=Constant(value=123, kind=None))
+            body=Constant(value=123))
 
 
 .. class:: FormattedValue(value, conversion, format_spec)
@@ -175,12 +175,11 @@ Literals
         Expression(
             body=JoinedStr(
                 values=[
-                    Constant(value='sin(', kind=None),
+                    Constant(value='sin('),
                     FormattedValue(
                         value=Name(id='a', ctx=Load()),
-                        conversion=-1,
-                        format_spec=None),
-                    Constant(value=') is ', kind=None),
+                        conversion=-1),
+                    Constant(value=') is '),
                     FormattedValue(
                         value=Call(
                             func=Name(id='sin', ctx=Load()),
@@ -190,7 +189,7 @@ Literals
                         conversion=-1,
                         format_spec=JoinedStr(
                             values=[
-                                Constant(value='.3', kind=None)]))]))
+                                Constant(value='.3')]))]))
 
 
 .. class:: List(elts, ctx)
@@ -206,17 +205,17 @@ Literals
         Expression(
             body=List(
                 elts=[
-                    Constant(value=1, kind=None),
-                    Constant(value=2, kind=None),
-                    Constant(value=3, kind=None)],
+                    Constant(value=1),
+                    Constant(value=2),
+                    Constant(value=3)],
                 ctx=Load()))
         >>> print(ast.dump(ast.parse('(1, 2, 3)', mode='eval'), indent=4))
         Expression(
             body=Tuple(
                 elts=[
-                    Constant(value=1, kind=None),
-                    Constant(value=2, kind=None),
-                    Constant(value=3, kind=None)],
+                    Constant(value=1),
+                    Constant(value=2),
+                    Constant(value=3)],
                 ctx=Load()))
 
 
@@ -230,9 +229,9 @@ Literals
         Expression(
             body=Set(
                 elts=[
-                    Constant(value=1, kind=None),
-                    Constant(value=2, kind=None),
-                    Constant(value=3, kind=None)]))
+                    Constant(value=1),
+                    Constant(value=2),
+                    Constant(value=3)]))
 
 
 .. class:: Dict(keys, values)
@@ -251,10 +250,10 @@ Literals
         Expression(
             body=Dict(
                 keys=[
-                    Constant(value='a', kind=None),
+                    Constant(value='a'),
                     None],
                 values=[
-                    Constant(value=1, kind=None),
+                    Constant(value=1),
                     Name(id='d', ctx=Load())]))
 
 
@@ -290,8 +289,7 @@ Variables
                 Assign(
                     targets=[
                         Name(id='a', ctx=Store())],
-                    value=Constant(value=1, kind=None),
-                    type_comment=None)],
+                    value=Constant(value=1))],
             type_ignores=[])
 
         >>> print(ast.dump(ast.parse('del a'), indent=4))
@@ -323,8 +321,7 @@ Variables
                                     value=Name(id='b', ctx=Store()),
                                     ctx=Store())],
                             ctx=Store())],
-                    value=Name(id='it', ctx=Load()),
-                    type_comment=None)],
+                    value=Name(id='it', ctx=Load()))],
             type_ignores=[])
 
 
@@ -442,13 +439,13 @@ Expressions
         >>> print(ast.dump(ast.parse('1 <= a < 10', mode='eval'), indent=4))
         Expression(
             body=Compare(
-                left=Constant(value=1, kind=None),
+                left=Constant(value=1),
                 ops=[
                     LtE(),
                     Lt()],
                 comparators=[
                     Name(id='a', ctx=Load()),
-                    Constant(value=10, kind=None)]))
+                    Constant(value=10)]))
 
 
 .. class:: Eq
@@ -493,7 +490,6 @@ Expressions
                         arg='b',
                         value=Name(id='c', ctx=Load())),
                     keyword(
-                        arg=None,
                         value=Name(id='e', ctx=Load()))]))
 
 
@@ -548,7 +544,7 @@ Expressions
         Expression(
             body=NamedExpr(
                 target=Name(id='x', ctx=Store()),
-                value=Constant(value=4, kind=None)))
+                value=Constant(value=4)))
 
 
 Subscripting
@@ -573,7 +569,7 @@ Subscripting
             body=Subscript(
                 value=Name(id='l', ctx=Load()),
                 slice=Index(
-                    value=Constant(value=1, kind=None)),
+                    value=Constant(value=1)),
                 ctx=Load()))
 
 
@@ -588,9 +584,8 @@ Subscripting
             body=Subscript(
                 value=Name(id='l', ctx=Load()),
                 slice=Slice(
-                    lower=Constant(value=1, kind=None),
-                    upper=Constant(value=2, kind=None),
-                    step=None),
+                    lower=Constant(value=1),
+                    upper=Constant(value=2)),
                 ctx=Load()))
 
 
@@ -608,11 +603,10 @@ Subscripting
                 slice=ExtSlice(
                     dims=[
                         Slice(
-                            lower=Constant(value=1, kind=None),
-                            upper=Constant(value=2, kind=None),
-                            step=None),
+                            lower=Constant(value=1),
+                            upper=Constant(value=2)),
                         Index(
-                            value=Constant(value=3, kind=None))]),
+                            value=Constant(value=3))]),
                 ctx=Load()))
 
 
@@ -649,7 +643,7 @@ Comprehensions
                 value=BinOp(
                     left=Name(id='x', ctx=Load()),
                     op=Pow(),
-                    right=Constant(value=2, kind=None)),
+                    right=Constant(value=2)),
                 generators=[
                     comprehension(
                         target=Name(id='x', ctx=Store()),
@@ -708,7 +702,7 @@ Comprehensions
                 elt=BinOp(
                     left=Name(id='n', ctx=Load()),
                     op=Pow(),
-                    right=Constant(value=2, kind=None)),
+                    right=Constant(value=2)),
                 generators=[
                     comprehension(
                         target=Name(id='n', ctx=Store()),
@@ -719,13 +713,13 @@ Comprehensions
                                 ops=[
                                     Gt()],
                                 comparators=[
-                                    Constant(value=5, kind=None)]),
+                                    Constant(value=5)]),
                             Compare(
                                 left=Name(id='n', ctx=Load()),
                                 ops=[
                                     Lt()],
                                 comparators=[
-                                    Constant(value=10, kind=None)])],
+                                    Constant(value=10)])],
                         is_async=0)]))
 
         >>> print(ast.dump(ast.parse('[i async for i in soc]', mode='eval'),
@@ -764,8 +758,7 @@ Statements
                     targets=[
                         Name(id='a', ctx=Store()),
                         Name(id='b', ctx=Store())],
-                    value=Constant(value=1, kind=None),
-                    type_comment=None)],
+                    value=Constant(value=1))],
             type_ignores=[])
 
         >>> print(ast.dump(ast.parse('a,b = c'), indent=4)) # Unpacking
@@ -778,8 +771,7 @@ Statements
                                 Name(id='a', ctx=Store()),
                                 Name(id='b', ctx=Store())],
                             ctx=Store())],
-                    value=Name(id='c', ctx=Load()),
-                    type_comment=None)],
+                    value=Name(id='c', ctx=Load()))],
             type_ignores=[])
 
 
@@ -800,7 +792,6 @@ Statements
                 AnnAssign(
                     target=Name(id='c', ctx=Store()),
                     annotation=Name(id='int', ctx=Load()),
-                    value=None,
                     simple=1)],
             type_ignores=[])
 
@@ -810,7 +801,7 @@ Statements
                 AnnAssign(
                     target=Name(id='a', ctx=Store()),
                     annotation=Name(id='int', ctx=Load()),
-                    value=Constant(value=1, kind=None),
+                    value=Constant(value=1),
                     simple=0)],
             type_ignores=[])
 
@@ -823,7 +814,6 @@ Statements
                         attr='b',
                         ctx=Store()),
                     annotation=Name(id='int', ctx=Load()),
-                    value=None,
                     simple=0)],
             type_ignores=[])
 
@@ -834,10 +824,9 @@ Statements
                     target=Subscript(
                         value=Name(id='a', ctx=Load()),
                         slice=Index(
-                            value=Constant(value=1, kind=None)),
+                            value=Constant(value=1)),
                         ctx=Store()),
                     annotation=Name(id='int', ctx=Load()),
-                    value=None,
                     simple=0)],
             type_ignores=[])
 
@@ -860,7 +849,7 @@ Statements
                 AugAssign(
                     target=Name(id='x', ctx=Store()),
                     op=Add(),
-                    value=Constant(value=2, kind=None))],
+                    value=Constant(value=2))],
             type_ignores=[])
 
 
@@ -945,9 +934,9 @@ Imports
             body=[
                 Import(
                     names=[
-                        alias(name='x', asname=None),
-                        alias(name='y', asname=None),
-                        alias(name='z', asname=None)])],
+                        alias(name='x'),
+                        alias(name='y'),
+                        alias(name='z')])],
             type_ignores=[])
 
 
@@ -966,9 +955,9 @@ Imports
                 ImportFrom(
                     module='y',
                     names=[
-                        alias(name='x', asname=None),
-                        alias(name='y', asname=None),
-                        alias(name='z', asname=None)],
+                        alias(name='x'),
+                        alias(name='y'),
+                        alias(name='z')],
                     level=0)],
             type_ignores=[])
 
@@ -987,7 +976,7 @@ Imports
                     module='foo.bar',
                     names=[
                         alias(name='a', asname='b'),
-                        alias(name='c', asname=None)],
+                        alias(name='c')],
                     level=2)],
             type_ignores=[])
 
@@ -1023,16 +1012,16 @@ Control flow
                     test=Name(id='x', ctx=Load()),
                     body=[
                         Expr(
-                            value=Constant(value=Ellipsis, kind=None))],
+                            value=Constant(value=Ellipsis))],
                     orelse=[
                         If(
                             test=Name(id='y', ctx=Load()),
                             body=[
                                 Expr(
-                                    value=Constant(value=Ellipsis, kind=None))],
+                                    value=Constant(value=Ellipsis))],
                             orelse=[
                                 Expr(
-                                    value=Constant(value=Ellipsis, kind=None))])])],
+                                    value=Constant(value=Ellipsis))])])],
             type_ignores=[])
 
 
@@ -1063,11 +1052,10 @@ Control flow
                     iter=Name(id='y', ctx=Load()),
                     body=[
                         Expr(
-                            value=Constant(value=Ellipsis, kind=None))],
+                            value=Constant(value=Ellipsis))],
                     orelse=[
                         Expr(
-                            value=Constant(value=Ellipsis, kind=None))],
-                    type_comment=None)],
+                            value=Constant(value=Ellipsis))])],
             type_ignores=[])
 
 
@@ -1090,10 +1078,10 @@ Control flow
                     test=Name(id='x', ctx=Load()),
                     body=[
                         Expr(
-                            value=Constant(value=Ellipsis, kind=None))],
+                            value=Constant(value=Ellipsis))],
                     orelse=[
                         Expr(
-                            value=Constant(value=Ellipsis, kind=None))])],
+                            value=Constant(value=Ellipsis))])],
             type_ignores=[])
 
 
@@ -1124,13 +1112,12 @@ Control flow
                                 ops=[
                                     Gt()],
                                 comparators=[
-                                    Constant(value=5, kind=None)]),
+                                    Constant(value=5)]),
                             body=[
                                 Break()],
                             orelse=[
                                 Continue()])],
-                    orelse=[],
-                    type_comment=None)],
+                    orelse=[])],
             type_ignores=[])
 
 
@@ -1158,26 +1145,25 @@ Control flow
                 Try(
                     body=[
                         Expr(
-                            value=Constant(value=Ellipsis, kind=None))],
+                            value=Constant(value=Ellipsis))],
                     handlers=[
                         ExceptHandler(
                             type=Name(id='Exception', ctx=Load()),
-                            name=None,
                             body=[
                                 Expr(
-                                    value=Constant(value=Ellipsis, kind=None))]),
+                                    value=Constant(value=Ellipsis))]),
                         ExceptHandler(
                             type=Name(id='OtherException', ctx=Load()),
                             name='e',
                             body=[
                                 Expr(
-                                    value=Constant(value=Ellipsis, kind=None))])],
+                                    value=Constant(value=Ellipsis))])],
                     orelse=[
                         Expr(
-                            value=Constant(value=Ellipsis, kind=None))],
+                            value=Constant(value=Ellipsis))],
                     finalbody=[
                         Expr(
-                            value=Constant(value=Ellipsis, kind=None))])],
+                            value=Constant(value=Ellipsis))])],
             type_ignores=[])
 
 
@@ -1204,11 +1190,10 @@ Control flow
                             value=BinOp(
                                 left=Name(id='a', ctx=Load()),
                                 op=Add(),
-                                right=Constant(value=1, kind=None)))],
+                                right=Constant(value=1)))],
                     handlers=[
                         ExceptHandler(
                             type=Name(id='TypeError', ctx=Load()),
-                            name=None,
                             body=[
                                 Pass()])],
                     orelse=[],
@@ -1256,8 +1241,7 @@ Control flow
                                 args=[
                                     Name(id='b', ctx=Load()),
                                     Name(id='d', ctx=Load())],
-                                keywords=[]))],
-                    type_comment=None)],
+                                keywords=[]))])],
             type_ignores=[])
 
 
@@ -1295,14 +1279,12 @@ Function and class definitions
                         args=arguments(
                             posonlyargs=[],
                             args=[
-                                arg(arg='x', annotation=None, type_comment=None),
-                                arg(arg='y', annotation=None, type_comment=None)],
-                            vararg=None,
+                                arg(arg='x'),
+                                arg(arg='y')],
                             kwonlyargs=[],
                             kw_defaults=[],
-                            kwarg=None,
                             defaults=[]),
-                        body=Constant(value=Ellipsis, kind=None)))],
+                        body=Constant(value=Ellipsis)))],
             type_ignores=[])
 
 
@@ -1347,28 +1329,26 @@ Function and class definitions
                         args=[
                             arg(
                                 arg='a',
-                                annotation=Constant(value='annotation', kind=None),
-                                type_comment=None),
-                            arg(arg='b', annotation=None, type_comment=None),
-                            arg(arg='c', annotation=None, type_comment=None)],
-                        vararg=arg(arg='d', annotation=None, type_comment=None),
+                                annotation=Constant(value='annotation')),
+                            arg(arg='b'),
+                            arg(arg='c')],
+                        vararg=arg(arg='d'),
                         kwonlyargs=[
-                            arg(arg='e', annotation=None, type_comment=None),
-                            arg(arg='f', annotation=None, type_comment=None)],
+                            arg(arg='e'),
+                            arg(arg='f')],
                         kw_defaults=[
                             None,
-                            Constant(value=3, kind=None)],
-                        kwarg=arg(arg='g', annotation=None, type_comment=None),
+                            Constant(value=3)],
+                        kwarg=arg(arg='g'),
                         defaults=[
-                            Constant(value=1, kind=None),
-                            Constant(value=2, kind=None)]),
+                            Constant(value=1),
+                            Constant(value=2)]),
                     body=[
                         Pass()],
                     decorator_list=[
                         Name(id='decorator1', ctx=Load()),
                         Name(id='decorator2', ctx=Load())],
-                    returns=Constant(value='return annotation', kind=None),
-                    type_comment=None)],
+                    returns=Constant(value='return annotation'))],
             type_ignores=[])
 
 
@@ -1382,7 +1362,7 @@ Function and class definitions
         Module(
             body=[
                 Return(
-                    value=Constant(value=4, kind=None))],
+                    value=Constant(value=4))],
             type_ignores=[])
 
 
@@ -1508,10 +1488,8 @@ Async and await
                 args=arguments(
                     posonlyargs=[],
                     args=[],
-                    vararg=None,
                     kwonlyargs=[],
                     kw_defaults=[],
-                    kwarg=None,
                     defaults=[]),
                 body=[
                     Expr(
@@ -1520,9 +1498,7 @@ Async and await
                                 func=Name(id='other_func', ctx=Load()),
                                 args=[],
                                 keywords=[])))],
-                decorator_list=[],
-                returns=None,
-                type_comment=None)],
+                decorator_list=[])],
         type_ignores=[])
 
 
diff --git a/Lib/ast.py b/Lib/ast.py
index 9a3d3806eb8ca..0bce4a49dc77a 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -123,31 +123,36 @@ def _format(node, level=0):
             prefix = ''
             sep = ', '
         if isinstance(node, AST):
+            cls = type(node)
             args = []
             allsimple = True
             keywords = annotate_fields
-            for field in node._fields:
+            for name in node._fields:
                 try:
-                    value = getattr(node, field)
+                    value = getattr(node, name)
                 except AttributeError:
                     keywords = True
+                    continue
+                if value is None and getattr(cls, name, ...) is None:
+                    keywords = True
+                    continue
+                value, simple = _format(value, level)
+                allsimple = allsimple and simple
+                if keywords:
+                    args.append('%s=%s' % (name, value))
                 else:
-                    value, simple = _format(value, level)
-                    allsimple = allsimple and simple
-                    if keywords:
-                        args.append('%s=%s' % (field, value))
-                    else:
-                        args.append(value)
+                    args.append(value)
             if include_attributes and node._attributes:
-                for attr in node._attributes:
+                for name in node._attributes:
                     try:
-                        value = getattr(node, attr)
+                        value = getattr(node, name)
                     except AttributeError:
-                        pass
-                    else:
-                        value, simple = _format(value, level)
-                        allsimple = allsimple and simple
-                        args.append('%s=%s' % (attr, value))
+                        continue
+                    if value is None and getattr(cls, name, ...) is None:
+                        continue
+                    value, simple = _format(value, level)
+                    allsimple = allsimple and simple
+                    args.append('%s=%s' % (name, value))
             if allsimple and len(args) <= 3:
                 return '%s(%s)' % (node.__class__.__name__, ', '.join(args)), not args
             return '%s(%s%s)' % (node.__class__.__name__, prefix, sep.join(args)), False
@@ -170,9 +175,10 @@ def copy_location(new_node, old_node):
     attributes) from *old_node* to *new_node* if possible, and return *new_node*.
     """
     for attr in 'lineno', 'col_offset', 'end_lineno', 'end_col_offset':
-        if attr in old_node._attributes and attr in new_node._attributes \
-           and hasattr(old_node, attr):
-            setattr(new_node, attr, getattr(old_node, attr))
+        if attr in old_node._attributes and attr in new_node._attributes:
+            value = getattr(old_node, attr, None)
+            if value is not None:
+                setattr(new_node, attr, value)
     return new_node
 
 
@@ -191,7 +197,7 @@ def _fix(node, lineno, col_offset, end_lineno, end_col_offset):
             else:
                 lineno = node.lineno
         if 'end_lineno' in node._attributes:
-            if not hasattr(node, 'end_lineno'):
+            if getattr(node, 'end_lineno', None) is None:
                 node.end_lineno = end_lineno
             else:
                 end_lineno = node.end_lineno
@@ -201,7 +207,7 @@ def _fix(node, lineno, col_offset, end_lineno, end_col_offset):
             else:
                 col_offset = node.col_offset
         if 'end_col_offset' in node._attributes:
-            if not hasattr(node, 'end_col_offset'):
+            if getattr(node, 'end_col_offset', None) is None:
                 node.end_col_offset = end_col_offset
             else:
                 end_col_offset = node.end_col_offset
diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py
index e78848537d47a..c1e9f00281115 100644
--- a/Lib/test/test_ast.py
+++ b/Lib/test/test_ast.py
@@ -353,9 +353,11 @@ def test_arguments(self):
                                      'kw_defaults', 'kwarg', 'defaults'))
 
         with self.assertRaises(AttributeError):
-            x.vararg
+            x.args
+        self.assertIsNone(x.vararg)
 
         x = ast.arguments(*range(1, 8))
+        self.assertEqual(x.args, 2)
         self.assertEqual(x.vararg, 3)
 
     def test_field_attr_writable(self):
@@ -650,18 +652,18 @@ def test_dump(self):
         node = ast.parse('spam(eggs, "and cheese")')
         self.assertEqual(ast.dump(node),
             "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load()), "
-            "args=[Name(id='eggs', ctx=Load()), Constant(value='and cheese', kind=None)], "
+            "args=[Name(id='eggs', ctx=Load()), Constant(value='and cheese')], "
             "keywords=[]))], type_ignores=[])"
         )
         self.assertEqual(ast.dump(node, annotate_fields=False),
             "Module([Expr(Call(Name('spam', Load()), [Name('eggs', Load()), "
-            "Constant('and cheese', None)], []))], [])"
+            "Constant('and cheese')], []))], [])"
         )
         self.assertEqual(ast.dump(node, include_attributes=True),
             "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), "
             "lineno=1, col_offset=0, end_lineno=1, end_col_offset=4), "
             "args=[Name(id='eggs', ctx=Load(), lineno=1, col_offset=5, "
-            "end_lineno=1, end_col_offset=9), Constant(value='and cheese', kind=None, "
+            "end_lineno=1, end_col_offset=9), Constant(value='and cheese', "
             "lineno=1, col_offset=11, end_lineno=1, end_col_offset=23)], keywords=[], "
             "lineno=1, col_offset=0, end_lineno=1, end_col_offset=24), "
             "lineno=1, col_offset=0, end_lineno=1, end_col_offset=24)], type_ignores=[])"
@@ -677,7 +679,7 @@ def test_dump_indent(self):
             func=Name(id='spam', ctx=Load()),
             args=[
                Name(id='eggs', ctx=Load()),
-               Constant(value='and cheese', kind=None)],
+               Constant(value='and cheese')],
             keywords=[]))],
    type_ignores=[])""")
         self.assertEqual(ast.dump(node, annotate_fields=False, indent='\t'), """\
@@ -688,7 +690,7 @@ def test_dump_indent(self):
 \t\t\t\tName('spam', Load()),
 \t\t\t\t[
 \t\t\t\t\tName('eggs', Load()),
-\t\t\t\t\tConstant('and cheese', None)],
+\t\t\t\t\tConstant('and cheese')],
 \t\t\t\t[]))],
 \t[])""")
         self.assertEqual(ast.dump(node, include_attributes=True, indent=3), """\
@@ -713,7 +715,6 @@ def test_dump_indent(self):
                   end_col_offset=9),
                Constant(
                   value='and cheese',
-                  kind=None,
                   lineno=1,
                   col_offset=11,
                   end_lineno=1,
@@ -762,7 +763,7 @@ def test_copy_location(self):
         src = ast.parse('1 + 1', mode='eval')
         src.body.right = ast.copy_location(ast.Num(2), src.body.right)
         self.assertEqual(ast.dump(src, include_attributes=True),
-            'Expression(body=BinOp(left=Constant(value=1, kind=None, lineno=1, col_offset=0, '
+            'Expression(body=BinOp(left=Constant(value=1, lineno=1, col_offset=0, '
             'end_lineno=1, end_col_offset=1), op=Add(), right=Constant(value=2, '
             'lineno=1, col_offset=4, end_lineno=1, end_col_offset=5), lineno=1, '
             'col_offset=0, end_lineno=1, end_col_offset=5))'
@@ -777,7 +778,7 @@ def test_fix_missing_locations(self):
         self.assertEqual(ast.dump(src, include_attributes=True),
             "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), "
             "lineno=1, col_offset=0, end_lineno=1, end_col_offset=5), "
-            "args=[Constant(value='spam', kind=None, lineno=1, col_offset=6, end_lineno=1, "
+            "args=[Constant(value='spam', lineno=1, col_offset=6, end_lineno=1, "
             "end_col_offset=12)], keywords=[], lineno=1, col_offset=0, end_lineno=1, "
             "end_col_offset=13), lineno=1, col_offset=0, end_lineno=1, "
             "end_col_offset=13), Expr(value=Call(func=Name(id='spam', ctx=Load(), "
@@ -792,8 +793,8 @@ def test_increment_lineno(self):
         src = ast.parse('1 + 1', mode='eval')
         self.assertEqual(ast.increment_lineno(src, n=3), src)
         self.assertEqual(ast.dump(src, include_attributes=True),
-            'Expression(body=BinOp(left=Constant(value=1, kind=None, lineno=4, col_offset=0, '
-            'end_lineno=4, end_col_offset=1), op=Add(), right=Constant(value=1, kind=None, '
+            'Expression(body=BinOp(left=Constant(value=1, lineno=4, col_offset=0, '
+            'end_lineno=4, end_col_offset=1), op=Add(), right=Constant(value=1, '
             'lineno=4, col_offset=4, end_lineno=4, end_col_offset=5), lineno=4, '
             'col_offset=0, end_lineno=4, end_col_offset=5))'
         )
@@ -801,8 +802,8 @@ def test_increment_lineno(self):
         src = ast.parse('1 + 1', mode='eval')
         self.assertEqual(ast.increment_lineno(src.body, n=3), src.body)
         self.assertEqual(ast.dump(src, include_attributes=True),
-            'Expression(body=BinOp(left=Constant(value=1, kind=None, lineno=4, col_offset=0, '
-            'end_lineno=4, end_col_offset=1), op=Add(), right=Constant(value=1, kind=None, '
+            'Expression(body=BinOp(left=Constant(value=1, lineno=4, col_offset=0, '
+            'end_lineno=4, end_col_offset=1), op=Add(), right=Constant(value=1, '
             'lineno=4, col_offset=4, end_lineno=4, end_col_offset=5), lineno=4, '
             'col_offset=0, end_lineno=4, end_col_offset=5))'
         )
@@ -821,7 +822,7 @@ def test_iter_child_nodes(self):
         self.assertEqual(next(iterator).value, 23)
         self.assertEqual(next(iterator).value, 42)
         self.assertEqual(ast.dump(next(iterator)),
-            "keyword(arg='eggs', value=Constant(value='leek', kind=None))"
+            "keyword(arg='eggs', value=Constant(value='leek'))"
         )
 
     def test_get_docstring(self):
diff --git a/Misc/NEWS.d/next/Library/2020-03-08-09-53-55.bpo-36287.mxr5m8.rst b/Misc/NEWS.d/next/Library/2020-03-08-09-53-55.bpo-36287.mxr5m8.rst
new file mode 100644
index 0000000000000..b7bef3cdf4912
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-03-08-09-53-55.bpo-36287.mxr5m8.rst
@@ -0,0 +1,4 @@
+:func:`ast.dump()` no longer outputs optional fields and attributes with
+default values. The default values for optional fields and attributes of
+AST nodes are now set as class attributes (e.g. ``Constant.kind`` is set
+to ``None``).
diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py
index e81506cc9a62d..016c12ddf1497 100755
--- a/Parser/asdl_c.py
+++ b/Parser/asdl_c.py
@@ -957,6 +957,8 @@ def visitProduct(self, prod, name):
                             (name, name, len(prod.attributes)), 1)
         else:
             self.emit("if (!add_attributes(state->%s_type, NULL, 0)) return 0;" % name, 1)
+        self.emit_defaults(name, prod.fields, 1)
+        self.emit_defaults(name, prod.attributes, 1)
 
     def visitSum(self, sum, name):
         self.emit('state->%s_type = make_type("%s", state->AST_type, NULL, 0);' %
@@ -968,6 +970,7 @@ def visitSum(self, sum, name):
                             (name, name, len(sum.attributes)), 1)
         else:
             self.emit("if (!add_attributes(state->%s_type, NULL, 0)) return 0;" % name, 1)
+        self.emit_defaults(name, sum.attributes, 1)
         simple = is_simple(sum)
         for t in sum.types:
             self.visitConstructor(t, name, simple)
@@ -981,12 +984,20 @@ def visitConstructor(self, cons, name, simple):
                             (cons.name, cons.name, name, fields, len(cons.fields)), 1)
         self.emit("if (!state->%s_type) return 0;" % cons.name, 1)
         self.emit_type("%s_type" % cons.name)
+        self.emit_defaults(cons.name, cons.fields, 1)
         if simple:
             self.emit("state->%s_singleton = PyType_GenericNew((PyTypeObject *)"
                       "state->%s_type, NULL, NULL);" %
                              (cons.name, cons.name), 1)
             self.emit("if (!state->%s_singleton) return 0;" % cons.name, 1)
 
+    def emit_defaults(self, name, fields, depth):
+        for field in fields:
+            if field.opt:
+                self.emit('if (PyObject_SetAttr(state->%s_type, state->%s, Py_None) == -1)' %
+                            (name, field.name), depth)
+                self.emit("return 0;", depth+1)
+
 
 class ASTModuleVisitor(PickleVisitor):
 
diff --git a/Python/Python-ast.c b/Python/Python-ast.c
index 2784c427d72dc..47c88b6741771 100644
--- a/Python/Python-ast.c
+++ b/Python/Python-ast.c
@@ -1439,58 +1439,102 @@ static int init_types(void)
     state->stmt_type = make_type("stmt", state->AST_type, NULL, 0);
     if (!state->stmt_type) return 0;
     if (!add_attributes(state->stmt_type, stmt_attributes, 4)) return 0;
+    if (PyObject_SetAttr(state->stmt_type, state->end_lineno, Py_None) == -1)
+        return 0;
+    if (PyObject_SetAttr(state->stmt_type, state->end_col_offset, Py_None) ==
+        -1)
+        return 0;
     state->FunctionDef_type = make_type("FunctionDef", state->stmt_type,
                                         FunctionDef_fields, 6);
     if (!state->FunctionDef_type) return 0;
+    if (PyObject_SetAttr(state->FunctionDef_type, state->returns, Py_None) ==
+        -1)
+        return 0;
+    if (PyObject_SetAttr(state->FunctionDef_type, state->type_comment, Py_None)
+        == -1)
+        return 0;
     state->AsyncFunctionDef_type = make_type("AsyncFunctionDef",
                                              state->stmt_type,
                                              AsyncFunctionDef_fields, 6);
     if (!state->AsyncFunctionDef_type) return 0;
+    if (PyObject_SetAttr(state->AsyncFunctionDef_type, state->returns, Py_None)
+        == -1)
+        return 0;
+    if (PyObject_SetAttr(state->AsyncFunctionDef_type, state->type_comment,
+        Py_None) == -1)
+        return 0;
     state->ClassDef_type = make_type("ClassDef", state->stmt_type,
                                      ClassDef_fields, 5);
     if (!state->ClassDef_type) return 0;
     state->Return_type = make_type("Return", state->stmt_type, Return_fields,
                                    1);
     if (!state->Return_type) return 0;
+    if (PyObject_SetAttr(state->Return_type, state->value, Py_None) == -1)
+        return 0;
     state->Delete_type = make_type("Delete", state->stmt_type, Delete_fields,
                                    1);
     if (!state->Delete_type) return 0;
     state->Assign_type = make_type("Assign", state->stmt_type, Assign_fields,
                                    3);
     if (!state->Assign_type) return 0;
+    if (PyObject_SetAttr(state->Assign_type, state->type_comment, Py_None) ==
+        -1)
+        return 0;
     state->AugAssign_type = make_type("AugAssign", state->stmt_type,
                                       AugAssign_fields, 3);
     if (!state->AugAssign_type) return 0;
     state->AnnAssign_type = make_type("AnnAssign", state->stmt_type,
                                       AnnAssign_fields, 4);
     if (!state->AnnAssign_type) return 0;
+    if (PyObject_SetAttr(state->AnnAssign_type, state->value, Py_None) == -1)
+        return 0;
     state->For_type = make_type("For", state->stmt_type, For_fields, 5);
     if (!state->For_type) return 0;
+    if (PyObject_SetAttr(state->For_type, state->type_comment, Py_None) == -1)
+        return 0;
     state->AsyncFor_type = make_type("AsyncFor", state->stmt_type,
                                      AsyncFor_fields, 5);
     if (!state->AsyncFor_type) return 0;
+    if (PyObject_SetAttr(state->AsyncFor_type, state->type_comment, Py_None) ==
+        -1)
+        return 0;
     state->While_type = make_type("While", state->stmt_type, While_fields, 3);
     if (!state->While_type) return 0;
     state->If_type = make_type("If", state->stmt_type, If_fields, 3);
     if (!state->If_type) return 0;
     state->With_type = make_type("With", state->stmt_type, With_fields, 3);
     if (!state->With_type) return 0;
+    if (PyObject_SetAttr(state->With_type, state->type_comment, Py_None) == -1)
+        return 0;
     state->AsyncWith_type = make_type("AsyncWith", state->stmt_type,
                                       AsyncWith_fields, 3);
     if (!state->AsyncWith_type) return 0;
+    if (PyObject_SetAttr(state->AsyncWith_type, state->type_comment, Py_None)
+        == -1)
+        return 0;
     state->Raise_type = make_type("Raise", state->stmt_type, Raise_fields, 2);
     if (!state->Raise_type) return 0;
+    if (PyObject_SetAttr(state->Raise_type, state->exc, Py_None) == -1)
+        return 0;
+    if (PyObject_SetAttr(state->Raise_type, state->cause, Py_None) == -1)
+        return 0;
     state->Try_type = make_type("Try", state->stmt_type, Try_fields, 4);
     if (!state->Try_type) return 0;
     state->Assert_type = make_type("Assert", state->stmt_type, Assert_fields,
                                    2);
     if (!state->Assert_type) return 0;
+    if (PyObject_SetAttr(state->Assert_type, state->msg, Py_None) == -1)
+        return 0;
     state->Import_type = make_type("Import", state->stmt_type, Import_fields,
                                    1);
     if (!state->Import_type) return 0;
     state->ImportFrom_type = make_type("ImportFrom", state->stmt_type,
                                        ImportFrom_fields, 3);
     if (!state->ImportFrom_type) return 0;
+    if (PyObject_SetAttr(state->ImportFrom_type, state->module, Py_None) == -1)
+        return 0;
+    if (PyObject_SetAttr(state->ImportFrom_type, state->level, Py_None) == -1)
+        return 0;
     state->Global_type = make_type("Global", state->stmt_type, Global_fields,
                                    1);
     if (!state->Global_type) return 0;
@@ -1508,6 +1552,11 @@ static int init_types(void)
     state->expr_type = make_type("expr", state->AST_type, NULL, 0);
     if (!state->expr_type) return 0;
     if (!add_attributes(state->expr_type, expr_attributes, 4)) return 0;
+    if (PyObject_SetAttr(state->expr_type, state->end_lineno, Py_None) == -1)
+        return 0;
+    if (PyObject_SetAttr(state->expr_type, state->end_col_offset, Py_None) ==
+        -1)
+        return 0;
     state->BoolOp_type = make_type("BoolOp", state->expr_type, BoolOp_fields,
                                    2);
     if (!state->BoolOp_type) return 0;
@@ -1544,6 +1593,8 @@ static int init_types(void)
     if (!state->Await_type) return 0;
     state->Yield_type = make_type("Yield", state->expr_type, Yield_fields, 1);
     if (!state->Yield_type) return 0;
+    if (PyObject_SetAttr(state->Yield_type, state->value, Py_None) == -1)
+        return 0;
     state->YieldFrom_type = make_type("YieldFrom", state->expr_type,
                                       YieldFrom_fields, 1);
     if (!state->YieldFrom_type) return 0;
@@ -1555,12 +1606,20 @@ static int init_types(void)
     state->FormattedValue_type = make_type("FormattedValue", state->expr_type,
                                            FormattedValue_fields, 3);
     if (!state->FormattedValue_type) return 0;
+    if (PyObject_SetAttr(state->FormattedValue_type, state->conversion,
+        Py_None) == -1)
+        return 0;
+    if (PyObject_SetAttr(state->FormattedValue_type, state->format_spec,
+        Py_None) == -1)
+        return 0;
     state->JoinedStr_type = make_type("JoinedStr", state->expr_type,
                                       JoinedStr_fields, 1);
     if (!state->JoinedStr_type) return 0;
     state->Constant_type = make_type("Constant", state->expr_type,
                                      Constant_fields, 2);
     if (!state->Constant_type) return 0;
+    if (PyObject_SetAttr(state->Constant_type, state->kind, Py_None) == -1)
+        return 0;
     state->Attribute_type = make_type("Attribute", state->expr_type,
                                       Attribute_fields, 3);
     if (!state->Attribute_type) return 0;
@@ -1619,6 +1678,12 @@ static int init_types(void)
     if (!add_attributes(state->slice_type, NULL, 0)) return 0;
     state->Slice_type = make_type("Slice", state->slice_type, Slice_fields, 3);
     if (!state->Slice_type) return 0;
+    if (PyObject_SetAttr(state->Slice_type, state->lower, Py_None) == -1)
+        return 0;
+    if (PyObject_SetAttr(state->Slice_type, state->upper, Py_None) == -1)
+        return 0;
+    if (PyObject_SetAttr(state->Slice_type, state->step, Py_None) == -1)
+        return 0;
     state->ExtSlice_type = make_type("ExtSlice", state->slice_type,
                                      ExtSlice_fields, 1);
     if (!state->ExtSlice_type) return 0;
@@ -1797,28 +1862,57 @@ static int init_types(void)
     if (!state->excepthandler_type) return 0;
     if (!add_attributes(state->excepthandler_type, excepthandler_attributes,
         4)) return 0;
+    if (PyObject_SetAttr(state->excepthandler_type, state->end_lineno, Py_None)
+        == -1)
+        return 0;
+    if (PyObject_SetAttr(state->excepthandler_type, state->end_col_offset,
+        Py_None) == -1)
+        return 0;
     state->ExceptHandler_type = make_type("ExceptHandler",
                                           state->excepthandler_type,
                                           ExceptHandler_fields, 3);
     if (!state->ExceptHandler_type) return 0;
+    if (PyObject_SetAttr(state->ExceptHandler_type, state->type, Py_None) == -1)
+        return 0;
+    if (PyObject_SetAttr(state->ExceptHandler_type, state->name, Py_None) == -1)
+        return 0;
     state->arguments_type = make_type("arguments", state->AST_type,
                                       arguments_fields, 7);
     if (!state->arguments_type) return 0;
     if (!add_attributes(state->arguments_type, NULL, 0)) return 0;
+    if (PyObject_SetAttr(state->arguments_type, state->vararg, Py_None) == -1)
+        return 0;
+    if (PyObject_SetAttr(state->arguments_type, state->kwarg, Py_None) == -1)
+        return 0;
     state->arg_type = make_type("arg", state->AST_type, arg_fields, 3);
     if (!state->arg_type) return 0;
     if (!add_attributes(state->arg_type, arg_attributes, 4)) return 0;
+    if (PyObject_SetAttr(state->arg_type, state->annotation, Py_None) == -1)
+        return 0;
+    if (PyObject_SetAttr(state->arg_type, state->type_comment, Py_None) == -1)
+        return 0;
+    if (PyObject_SetAttr(state->arg_type, state->end_lineno, Py_None) == -1)
+        return 0;
+    if (PyObject_SetAttr(state->arg_type, state->end_col_offset, Py_None) == -1)
+        return 0;
     state->keyword_type = make_type("keyword", state->AST_type, keyword_fields,
                                     2);
     if (!state->keyword_type) return 0;
     if (!add_attributes(state->keyword_type, NULL, 0)) return 0;
+    if (PyObject_SetAttr(state->keyword_type, state->arg, Py_None) == -1)
+        return 0;
     state->alias_type = make_type("alias", state->AST_type, alias_fields, 2);
     if (!state->alias_type) return 0;
     if (!add_attributes(state->alias_type, NULL, 0)) return 0;
+    if (PyObject_SetAttr(state->alias_type, state->asname, Py_None) == -1)
+        return 0;
     state->withitem_type = make_type("withitem", state->AST_type,
                                      withitem_fields, 2);
     if (!state->withitem_type) return 0;
     if (!add_attributes(state->withitem_type, NULL, 0)) return 0;
+    if (PyObject_SetAttr(state->withitem_type, state->optional_vars, Py_None)
+        == -1)
+        return 0;
     state->type_ignore_type = make_type("type_ignore", state->AST_type, NULL,
                                         0);
     if (!state->type_ignore_type) return 0;



More information about the Python-checkins mailing list