]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-116023: Add `show_empty=False` to `ast.dump` (#116037)
authorNikita Sobolev <mail@sobolevn.me>
Wed, 24 Apr 2024 08:02:38 +0000 (11:02 +0300)
committerGitHub <noreply@github.com>
Wed, 24 Apr 2024 08:02:38 +0000 (11:02 +0300)
Co-authored-by: Carl Meyer <carl@oddbird.net>
Doc/library/ast.rst
Lib/ast.py
Lib/test/test_ast.py
Misc/NEWS.d/next/Library/2024-02-28-11-51-51.gh-issue-116023.CGYhFh.rst [new file with mode: 0644]

index 8d407321092eef17900d24902895c5b31cd73b3d..09f2a404786fb6b6e66edc3e03f9489130f8a513 100644 (file)
@@ -173,8 +173,7 @@ Root nodes
                 Assign(
                     targets=[
                         Name(id='x', ctx=Store())],
-                    value=Constant(value=1))],
-            type_ignores=[])
+                    value=Constant(value=1))])
 
 
 .. class:: Expression(body)
@@ -302,8 +301,7 @@ Literals
                         value=Call(
                             func=Name(id='sin', ctx=Load()),
                             args=[
-                                Name(id='a', ctx=Load())],
-                            keywords=[]),
+                                Name(id='a', ctx=Load())]),
                         conversion=-1,
                         format_spec=JoinedStr(
                             values=[
@@ -398,8 +396,7 @@ Variables
         Module(
             body=[
                 Expr(
-                    value=Name(id='a', ctx=Load()))],
-            type_ignores=[])
+                    value=Name(id='a', ctx=Load()))])
 
         >>> print(ast.dump(ast.parse('a = 1'), indent=4))
         Module(
@@ -407,16 +404,14 @@ Variables
                 Assign(
                     targets=[
                         Name(id='a', ctx=Store())],
-                    value=Constant(value=1))],
-            type_ignores=[])
+                    value=Constant(value=1))])
 
         >>> print(ast.dump(ast.parse('del a'), indent=4))
         Module(
             body=[
                 Delete(
                     targets=[
-                        Name(id='a', ctx=Del())])],
-            type_ignores=[])
+                        Name(id='a', ctx=Del())])])
 
 
 .. class:: Starred(value, ctx)
@@ -439,8 +434,7 @@ Variables
                                     value=Name(id='b', ctx=Store()),
                                     ctx=Store())],
                             ctx=Store())],
-                    value=Name(id='it', ctx=Load()))],
-            type_ignores=[])
+                    value=Name(id='it', ctx=Load()))])
 
 
 .. _ast-expressions:
@@ -463,8 +457,7 @@ Expressions
                 Expr(
                     value=UnaryOp(
                         op=USub(),
-                        operand=Name(id='a', ctx=Load())))],
-            type_ignores=[])
+                        operand=Name(id='a', ctx=Load())))])
 
 
 .. class:: UnaryOp(op, operand)
@@ -729,7 +722,10 @@ Comprehensions
 
    .. doctest::
 
-        >>> print(ast.dump(ast.parse('[x for x in numbers]', mode='eval'), indent=4))
+        >>> print(ast.dump(
+        ...     ast.parse('[x for x in numbers]', mode='eval'),
+        ...     indent=4,
+        ... ))
         Expression(
             body=ListComp(
                 elt=Name(id='x', ctx=Load()),
@@ -737,9 +733,11 @@ Comprehensions
                     comprehension(
                         target=Name(id='x', ctx=Store()),
                         iter=Name(id='numbers', ctx=Load()),
-                        ifs=[],
                         is_async=0)]))
-        >>> print(ast.dump(ast.parse('{x: x**2 for x in numbers}', mode='eval'), indent=4))
+        >>> print(ast.dump(
+        ...     ast.parse('{x: x**2 for x in numbers}', mode='eval'),
+        ...     indent=4,
+        ... ))
         Expression(
             body=DictComp(
                 key=Name(id='x', ctx=Load()),
@@ -751,9 +749,11 @@ Comprehensions
                     comprehension(
                         target=Name(id='x', ctx=Store()),
                         iter=Name(id='numbers', ctx=Load()),
-                        ifs=[],
                         is_async=0)]))
-        >>> print(ast.dump(ast.parse('{x for x in numbers}', mode='eval'), indent=4))
+        >>> print(ast.dump(
+        ...     ast.parse('{x for x in numbers}', mode='eval'),
+        ...     indent=4,
+        ... ))
         Expression(
             body=SetComp(
                 elt=Name(id='x', ctx=Load()),
@@ -761,7 +761,6 @@ Comprehensions
                     comprehension(
                         target=Name(id='x', ctx=Store()),
                         iter=Name(id='numbers', ctx=Load()),
-                        ifs=[],
                         is_async=0)]))
 
 
@@ -784,18 +783,15 @@ Comprehensions
                 elt=Call(
                     func=Name(id='ord', ctx=Load()),
                     args=[
-                        Name(id='c', ctx=Load())],
-                    keywords=[]),
+                        Name(id='c', ctx=Load())]),
                 generators=[
                     comprehension(
                         target=Name(id='line', ctx=Store()),
                         iter=Name(id='file', ctx=Load()),
-                        ifs=[],
                         is_async=0),
                     comprehension(
                         target=Name(id='c', ctx=Store()),
                         iter=Name(id='line', ctx=Load()),
-                        ifs=[],
                         is_async=0)]))
 
         >>> print(ast.dump(ast.parse('(n**2 for n in it if n>5 if n<10)', mode='eval'),
@@ -834,7 +830,6 @@ Comprehensions
                     comprehension(
                         target=Name(id='i', ctx=Store()),
                         iter=Name(id='soc', ctx=Load()),
-                        ifs=[],
                         is_async=1)]))
 
 
@@ -864,8 +859,7 @@ Statements
                     targets=[
                         Name(id='a', ctx=Store()),
                         Name(id='b', ctx=Store())],
-                    value=Constant(value=1))],
-            type_ignores=[])
+                    value=Constant(value=1))])
 
         >>> print(ast.dump(ast.parse('a,b = c'), indent=4)) # Unpacking
         Module(
@@ -877,8 +871,7 @@ Statements
                                 Name(id='a', ctx=Store()),
                                 Name(id='b', ctx=Store())],
                             ctx=Store())],
-                    value=Name(id='c', ctx=Load()))],
-            type_ignores=[])
+                    value=Name(id='c', ctx=Load()))])
 
 
 .. class:: AnnAssign(target, annotation, value, simple)
@@ -898,8 +891,7 @@ Statements
                 AnnAssign(
                     target=Name(id='c', ctx=Store()),
                     annotation=Name(id='int', ctx=Load()),
-                    simple=1)],
-            type_ignores=[])
+                    simple=1)])
 
         >>> print(ast.dump(ast.parse('(a): int = 1'), indent=4)) # Annotation with parenthesis
         Module(
@@ -908,8 +900,7 @@ Statements
                     target=Name(id='a', ctx=Store()),
                     annotation=Name(id='int', ctx=Load()),
                     value=Constant(value=1),
-                    simple=0)],
-            type_ignores=[])
+                    simple=0)])
 
         >>> print(ast.dump(ast.parse('a.b: int'), indent=4)) # Attribute annotation
         Module(
@@ -920,8 +911,7 @@ Statements
                         attr='b',
                         ctx=Store()),
                     annotation=Name(id='int', ctx=Load()),
-                    simple=0)],
-            type_ignores=[])
+                    simple=0)])
 
         >>> print(ast.dump(ast.parse('a[1]: int'), indent=4)) # Subscript annotation
         Module(
@@ -932,8 +922,7 @@ Statements
                         slice=Constant(value=1),
                         ctx=Store()),
                     annotation=Name(id='int', ctx=Load()),
-                    simple=0)],
-            type_ignores=[])
+                    simple=0)])
 
 
 .. class:: AugAssign(target, op, value)
@@ -954,8 +943,7 @@ Statements
                 AugAssign(
                     target=Name(id='x', ctx=Store()),
                     op=Add(),
-                    value=Constant(value=2))],
-            type_ignores=[])
+                    value=Constant(value=2))])
 
 
 .. class:: Raise(exc, cause)
@@ -971,8 +959,7 @@ Statements
             body=[
                 Raise(
                     exc=Name(id='x', ctx=Load()),
-                    cause=Name(id='y', ctx=Load()))],
-            type_ignores=[])
+                    cause=Name(id='y', ctx=Load()))])
 
 
 .. class:: Assert(test, msg)
@@ -987,8 +974,7 @@ Statements
             body=[
                 Assert(
                     test=Name(id='x', ctx=Load()),
-                    msg=Name(id='y', ctx=Load()))],
-            type_ignores=[])
+                    msg=Name(id='y', ctx=Load()))])
 
 
 .. class:: Delete(targets)
@@ -1005,8 +991,7 @@ Statements
                     targets=[
                         Name(id='x', ctx=Del()),
                         Name(id='y', ctx=Del()),
-                        Name(id='z', ctx=Del())])],
-            type_ignores=[])
+                        Name(id='z', ctx=Del())])])
 
 
 .. class:: Pass()
@@ -1018,8 +1003,7 @@ Statements
         >>> print(ast.dump(ast.parse('pass'), indent=4))
         Module(
             body=[
-                Pass()],
-            type_ignores=[])
+                Pass()])
 
 
 .. class:: TypeAlias(name, type_params, value)
@@ -1036,9 +1020,7 @@ Statements
             body=[
                 TypeAlias(
                     name=Name(id='Alias', ctx=Store()),
-                    type_params=[],
-                    value=Name(id='int', ctx=Load()))],
-            type_ignores=[])
+                    value=Name(id='int', ctx=Load()))])
 
    .. versionadded:: 3.12
 
@@ -1061,8 +1043,7 @@ Imports
                     names=[
                         alias(name='x'),
                         alias(name='y'),
-                        alias(name='z')])],
-            type_ignores=[])
+                        alias(name='z')])])
 
 
 .. class:: ImportFrom(module, names, level)
@@ -1083,8 +1064,7 @@ Imports
                         alias(name='x'),
                         alias(name='y'),
                         alias(name='z')],
-                    level=0)],
-            type_ignores=[])
+                    level=0)])
 
 
 .. class:: alias(name, asname)
@@ -1102,8 +1082,7 @@ Imports
                     names=[
                         alias(name='a', asname='b'),
                         alias(name='c')],
-                    level=2)],
-            type_ignores=[])
+                    level=2)])
 
 Control flow
 ^^^^^^^^^^^^
@@ -1146,8 +1125,7 @@ Control flow
                                     value=Constant(value=Ellipsis))],
                             orelse=[
                                 Expr(
-                                    value=Constant(value=Ellipsis))])])],
-            type_ignores=[])
+                                    value=Constant(value=Ellipsis))])])])
 
 
 .. class:: For(target, iter, body, orelse, type_comment)
@@ -1181,8 +1159,7 @@ Control flow
                             value=Constant(value=Ellipsis))],
                     orelse=[
                         Expr(
-                            value=Constant(value=Ellipsis))])],
-            type_ignores=[])
+                            value=Constant(value=Ellipsis))])])
 
 
 .. class:: While(test, body, orelse)
@@ -1207,8 +1184,7 @@ Control flow
                             value=Constant(value=Ellipsis))],
                     orelse=[
                         Expr(
-                            value=Constant(value=Ellipsis))])],
-            type_ignores=[])
+                            value=Constant(value=Ellipsis))])])
 
 
 .. class:: Break
@@ -1242,9 +1218,7 @@ Control flow
                             body=[
                                 Break()],
                             orelse=[
-                                Continue()])],
-                    orelse=[])],
-            type_ignores=[])
+                                Continue()])])])
 
 
 .. class:: Try(body, handlers, orelse, finalbody)
@@ -1289,8 +1263,7 @@ Control flow
                             value=Constant(value=Ellipsis))],
                     finalbody=[
                         Expr(
-                            value=Constant(value=Ellipsis))])],
-            type_ignores=[])
+                            value=Constant(value=Ellipsis))])])
 
 
 .. class:: TryStar(body, handlers, orelse, finalbody)
@@ -1318,10 +1291,7 @@ Control flow
                             type=Name(id='Exception', ctx=Load()),
                             body=[
                                 Expr(
-                                    value=Constant(value=Ellipsis))])],
-                    orelse=[],
-                    finalbody=[])],
-            type_ignores=[])
+                                    value=Constant(value=Ellipsis))])])])
 
    .. versionadded:: 3.11
 
@@ -1353,10 +1323,7 @@ Control flow
                         ExceptHandler(
                             type=Name(id='TypeError', ctx=Load()),
                             body=[
-                                Pass()])],
-                    orelse=[],
-                    finalbody=[])],
-            type_ignores=[])
+                                Pass()])])])
 
 
 .. class:: With(items, body, type_comment)
@@ -1398,9 +1365,7 @@ Control flow
                                 func=Name(id='something', ctx=Load()),
                                 args=[
                                     Name(id='b', ctx=Load()),
-                                    Name(id='d', ctx=Load())],
-                                keywords=[]))])],
-            type_ignores=[])
+                                    Name(id='d', ctx=Load())]))])])
 
 
 Pattern matching
@@ -1457,14 +1422,10 @@ Pattern matching
                                     value=Constant(value=Ellipsis))]),
                         match_case(
                             pattern=MatchClass(
-                                cls=Name(id='tuple', ctx=Load()),
-                                patterns=[],
-                                kwd_attrs=[],
-                                kwd_patterns=[]),
+                                cls=Name(id='tuple', ctx=Load())),
                             body=[
                                 Expr(
-                                    value=Constant(value=Ellipsis))])])],
-            type_ignores=[])
+                                    value=Constant(value=Ellipsis))])])])
 
    .. versionadded:: 3.10
 
@@ -1492,8 +1453,7 @@ Pattern matching
                                 value=Constant(value='Relevant')),
                             body=[
                                 Expr(
-                                    value=Constant(value=Ellipsis))])])],
-            type_ignores=[])
+                                    value=Constant(value=Ellipsis))])])])
 
    .. versionadded:: 3.10
 
@@ -1519,8 +1479,7 @@ Pattern matching
                             pattern=MatchSingleton(value=None),
                             body=[
                                 Expr(
-                                    value=Constant(value=Ellipsis))])])],
-            type_ignores=[])
+                                    value=Constant(value=Ellipsis))])])])
 
    .. versionadded:: 3.10
 
@@ -1552,8 +1511,7 @@ Pattern matching
                                         value=Constant(value=2))]),
                             body=[
                                 Expr(
-                                    value=Constant(value=Ellipsis))])])],
-            type_ignores=[])
+                                    value=Constant(value=Ellipsis))])])])
 
    .. versionadded:: 3.10
 
@@ -1594,8 +1552,7 @@ Pattern matching
                                     MatchStar()]),
                             body=[
                                 Expr(
-                                    value=Constant(value=Ellipsis))])])],
-            type_ignores=[])
+                                    value=Constant(value=Ellipsis))])])])
 
    .. versionadded:: 3.10
 
@@ -1639,11 +1596,10 @@ Pattern matching
                                 Expr(
                                     value=Constant(value=Ellipsis))]),
                         match_case(
-                            pattern=MatchMapping(keys=[], patterns=[], rest='rest'),
+                            pattern=MatchMapping(rest='rest'),
                             body=[
                                 Expr(
-                                    value=Constant(value=Ellipsis))])])],
-            type_ignores=[])
+                                    value=Constant(value=Ellipsis))])])])
 
    .. versionadded:: 3.10
 
@@ -1685,16 +1641,13 @@ Pattern matching
                                     MatchValue(
                                         value=Constant(value=0)),
                                     MatchValue(
-                                        value=Constant(value=0))],
-                                kwd_attrs=[],
-                                kwd_patterns=[]),
+                                        value=Constant(value=0))]),
                             body=[
                                 Expr(
                                     value=Constant(value=Ellipsis))]),
                         match_case(
                             pattern=MatchClass(
                                 cls=Name(id='Point3D', ctx=Load()),
-                                patterns=[],
                                 kwd_attrs=[
                                     'x',
                                     'y',
@@ -1708,8 +1661,7 @@ Pattern matching
                                         value=Constant(value=0))]),
                             body=[
                                 Expr(
-                                    value=Constant(value=Ellipsis))])])],
-            type_ignores=[])
+                                    value=Constant(value=Ellipsis))])])])
 
    .. versionadded:: 3.10
 
@@ -1751,8 +1703,7 @@ Pattern matching
                             pattern=MatchAs(),
                             body=[
                                 Expr(
-                                    value=Constant(value=Ellipsis))])])],
-            type_ignores=[])
+                                    value=Constant(value=Ellipsis))])])])
 
    .. versionadded:: 3.10
 
@@ -1785,8 +1736,7 @@ Pattern matching
                                     MatchAs(name='y')]),
                             body=[
                                 Expr(
-                                    value=Constant(value=Ellipsis))])])],
-            type_ignores=[])
+                                    value=Constant(value=Ellipsis))])])])
 
    .. versionadded:: 3.10
 
@@ -1818,8 +1768,7 @@ aliases.
                     value=Subscript(
                         value=Name(id='list', ctx=Load()),
                         slice=Name(id='T', ctx=Load()),
-                        ctx=Load()))],
-            type_ignores=[])
+                        ctx=Load()))])
 
    .. versionadded:: 3.12
 
@@ -1843,8 +1792,7 @@ aliases.
                                 Name(id='P', ctx=Load()),
                                 Name(id='int', ctx=Load())],
                             ctx=Load()),
-                        ctx=Load()))],
-            type_ignores=[])
+                        ctx=Load()))])
 
    .. versionadded:: 3.12
 
@@ -1869,8 +1817,7 @@ aliases.
                                     value=Name(id='Ts', ctx=Load()),
                                     ctx=Load())],
                             ctx=Load()),
-                        ctx=Load()))],
-            type_ignores=[])
+                        ctx=Load()))])
 
    .. versionadded:: 3.12
 
@@ -1910,15 +1857,10 @@ Function and class definitions
                 Expr(
                     value=Lambda(
                         args=arguments(
-                            posonlyargs=[],
                             args=[
                                 arg(arg='x'),
-                                arg(arg='y')],
-                            kwonlyargs=[],
-                            kw_defaults=[],
-                            defaults=[]),
-                        body=Constant(value=Ellipsis)))],
-            type_ignores=[])
+                                arg(arg='y')]),
+                        body=Constant(value=Ellipsis)))])
 
 
 .. class:: arguments(posonlyargs, args, vararg, kwonlyargs, kw_defaults, kwarg, defaults)
@@ -1957,7 +1899,6 @@ Function and class definitions
                 FunctionDef(
                     name='f',
                     args=arguments(
-                        posonlyargs=[],
                         args=[
                             arg(
                                 arg='a',
@@ -1980,9 +1921,7 @@ Function and class definitions
                     decorator_list=[
                         Name(id='decorator1', ctx=Load()),
                         Name(id='decorator2', ctx=Load())],
-                    returns=Constant(value='return annotation'),
-                    type_params=[])],
-            type_ignores=[])
+                    returns=Constant(value='return annotation'))])
 
 
 .. class:: Return(value)
@@ -1995,8 +1934,7 @@ Function and class definitions
         Module(
             body=[
                 Return(
-                    value=Constant(value=4))],
-            type_ignores=[])
+                    value=Constant(value=4))])
 
 
 .. class:: Yield(value)
@@ -2012,16 +1950,14 @@ Function and class definitions
             body=[
                 Expr(
                     value=Yield(
-                        value=Name(id='x', ctx=Load())))],
-            type_ignores=[])
+                        value=Name(id='x', ctx=Load())))])
 
         >>> print(ast.dump(ast.parse('yield from x'), indent=4))
         Module(
             body=[
                 Expr(
                     value=YieldFrom(
-                        value=Name(id='x', ctx=Load())))],
-            type_ignores=[])
+                        value=Name(id='x', ctx=Load())))])
 
 
 .. class:: Global(names)
@@ -2038,8 +1974,7 @@ Function and class definitions
                     names=[
                         'x',
                         'y',
-                        'z'])],
-            type_ignores=[])
+                        'z'])])
 
         >>> print(ast.dump(ast.parse('nonlocal x,y,z'), indent=4))
         Module(
@@ -2048,8 +1983,7 @@ Function and class definitions
                     names=[
                         'x',
                         'y',
-                        'z'])],
-            type_ignores=[])
+                        'z'])])
 
 
 .. class:: ClassDef(name, bases, keywords, body, decorator_list, type_params)
@@ -2089,9 +2023,7 @@ Function and class definitions
                         Pass()],
                     decorator_list=[
                         Name(id='decorator1', ctx=Load()),
-                        Name(id='decorator2', ctx=Load())],
-                    type_params=[])],
-            type_ignores=[])
+                        Name(id='decorator2', ctx=Load())])])
 
    .. versionchanged:: 3.12
         Added ``type_params``.
@@ -2123,22 +2055,12 @@ Async and await
         body=[
             AsyncFunctionDef(
                 name='f',
-                args=arguments(
-                    posonlyargs=[],
-                    args=[],
-                    kwonlyargs=[],
-                    kw_defaults=[],
-                    defaults=[]),
+                args=arguments(),
                 body=[
                     Expr(
                         value=Await(
                             value=Call(
-                                func=Name(id='other_func', ctx=Load()),
-                                args=[],
-                                keywords=[])))],
-                decorator_list=[],
-                type_params=[])],
-        type_ignores=[])
+                                func=Name(id='other_func', ctx=Load()))))])])
 
 
 .. class:: AsyncFor(target, iter, body, orelse, type_comment)
@@ -2425,7 +2347,7 @@ and classes for traversing abstract syntax trees:
       node = YourTransformer().visit(node)
 
 
-.. function:: dump(node, annotate_fields=True, include_attributes=False, *, indent=None)
+.. function:: dump(node, annotate_fields=True, include_attributes=False, *, indent=None, show_empty=False)
 
    Return a formatted dump of the tree in *node*.  This is mainly useful for
    debugging purposes.  If *annotate_fields* is true (by default),
@@ -2442,9 +2364,42 @@ and classes for traversing abstract syntax trees:
    indents that many spaces per level.  If *indent* is a string (such as ``"\t"``),
    that string is used to indent each level.
 
+   If *show_empty* is ``False`` (the default), empty lists and fields that are ``None``
+   will be omitted from the output.
+
    .. versionchanged:: 3.9
       Added the *indent* option.
 
+   .. versionchanged:: 3.13
+      Added the *show_empty* option.
+
+      .. doctest::
+
+         >>> print(ast.dump(ast.parse("""\
+         ... async def f():
+         ...     await other_func()
+         ... """), indent=4, show_empty=True))
+         Module(
+             body=[
+                 AsyncFunctionDef(
+                     name='f',
+                     args=arguments(
+                         posonlyargs=[],
+                         args=[],
+                         kwonlyargs=[],
+                         kw_defaults=[],
+                         defaults=[]),
+                     body=[
+                         Expr(
+                             value=Await(
+                                 value=Call(
+                                     func=Name(id='other_func', ctx=Load()),
+                                     args=[],
+                                     keywords=[])))],
+                     decorator_list=[],
+                     type_params=[])],
+             type_ignores=[])
+
 
 .. _ast-compiler-flags:
 
index b8c4ce6f919e6b4c5e7614a415cf4ffc65d17fe0..9f386051659e76caad71fa7d4226b534e16ddac1 100644 (file)
@@ -114,7 +114,11 @@ def literal_eval(node_or_string):
     return _convert(node_or_string)
 
 
-def dump(node, annotate_fields=True, include_attributes=False, *, indent=None):
+def dump(
+    node, annotate_fields=True, include_attributes=False,
+    *,
+    indent=None, show_empty=False,
+):
     """
     Return a formatted dump of the tree in node.  This is mainly useful for
     debugging purposes.  If annotate_fields is true (by default),
@@ -125,6 +129,8 @@ def dump(node, annotate_fields=True, include_attributes=False, *, indent=None):
     include_attributes can be set to true.  If indent is a non-negative
     integer or string, then the tree will be pretty-printed with that indent
     level. None (the default) selects the single line representation.
+    If show_empty is False, then empty lists and fields that are None
+    will be omitted from the output for better readability.
     """
     def _format(node, level=0):
         if indent is not None:
@@ -137,6 +143,7 @@ def dump(node, annotate_fields=True, include_attributes=False, *, indent=None):
         if isinstance(node, AST):
             cls = type(node)
             args = []
+            args_buffer = []
             allsimple = True
             keywords = annotate_fields
             for name in node._fields:
@@ -148,6 +155,18 @@ def dump(node, annotate_fields=True, include_attributes=False, *, indent=None):
                 if value is None and getattr(cls, name, ...) is None:
                     keywords = True
                     continue
+                if (
+                    not show_empty
+                    and (value is None or value == [])
+                    # Special cases:
+                    # `Constant(value=None)` and `MatchSingleton(value=None)`
+                    and not isinstance(node, (Constant, MatchSingleton))
+                ):
+                    args_buffer.append(repr(value))
+                    continue
+                elif not keywords:
+                    args.extend(args_buffer)
+                    args_buffer = []
                 value, simple = _format(value, level)
                 allsimple = allsimple and simple
                 if keywords:
index 5b47cdaafb092e4de92f7aaa6437a1438be03d44..44bcb9bae1cfde989fca941c675f58ec26ee9511 100644 (file)
@@ -1227,21 +1227,20 @@ class ASTHelpers_Test(unittest.TestCase):
         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')], "
-            "keywords=[]))], type_ignores=[])"
+            "args=[Name(id='eggs', ctx=Load()), Constant(value='and cheese')]))])"
         )
         self.assertEqual(ast.dump(node, annotate_fields=False),
             "Module([Expr(Call(Name('spam', Load()), [Name('eggs', Load()), "
-            "Constant('and cheese')], []))], [])"
+            "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', "
-            "lineno=1, col_offset=11, end_lineno=1, end_col_offset=23)], keywords=[], "
+            "lineno=1, col_offset=11, end_lineno=1, end_col_offset=23)], "
             "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=[])"
+            "lineno=1, col_offset=0, end_lineno=1, end_col_offset=24)])"
         )
 
     def test_dump_indent(self):
@@ -1254,9 +1253,7 @@ Module(
             func=Name(id='spam', ctx=Load()),
             args=[
                Name(id='eggs', ctx=Load()),
-               Constant(value='and cheese')],
-            keywords=[]))],
-   type_ignores=[])""")
+               Constant(value='and cheese')]))])""")
         self.assertEqual(ast.dump(node, annotate_fields=False, indent='\t'), """\
 Module(
 \t[
@@ -1265,9 +1262,7 @@ Module(
 \t\t\t\tName('spam', Load()),
 \t\t\t\t[
 \t\t\t\t\tName('eggs', Load()),
-\t\t\t\t\tConstant('and cheese')],
-\t\t\t\t[]))],
-\t[])""")
+\t\t\t\t\tConstant('and cheese')]))])""")
         self.assertEqual(ast.dump(node, include_attributes=True, indent=3), """\
 Module(
    body=[
@@ -1294,7 +1289,6 @@ Module(
                   col_offset=11,
                   end_lineno=1,
                   end_col_offset=23)],
-            keywords=[],
             lineno=1,
             col_offset=0,
             end_lineno=1,
@@ -1302,8 +1296,7 @@ Module(
          lineno=1,
          col_offset=0,
          end_lineno=1,
-         end_col_offset=24)],
-   type_ignores=[])""")
+         end_col_offset=24)])""")
 
     def test_dump_incomplete(self):
         node = ast.Raise(lineno=3, col_offset=4)
@@ -1333,6 +1326,119 @@ Module(
         self.assertEqual(ast.dump(node, annotate_fields=False),
             "Raise(cause=Name('e', Load()))"
         )
+        # Arguments:
+        node = ast.arguments(args=[ast.arg("x")])
+        self.assertEqual(ast.dump(node, annotate_fields=False),
+            "arguments([], [arg('x')])",
+        )
+        node = ast.arguments(posonlyargs=[ast.arg("x")])
+        self.assertEqual(ast.dump(node, annotate_fields=False),
+            "arguments([arg('x')])",
+        )
+        node = ast.arguments(posonlyargs=[ast.arg("x")], kwonlyargs=[ast.arg('y')])
+        self.assertEqual(ast.dump(node, annotate_fields=False),
+            "arguments([arg('x')], kwonlyargs=[arg('y')])",
+        )
+        node = ast.arguments(args=[ast.arg("x")], kwonlyargs=[ast.arg('y')])
+        self.assertEqual(ast.dump(node, annotate_fields=False),
+            "arguments([], [arg('x')], kwonlyargs=[arg('y')])",
+        )
+        node = ast.arguments()
+        self.assertEqual(ast.dump(node, annotate_fields=False),
+            "arguments()",
+        )
+        # Classes:
+        node = ast.ClassDef(
+            'T',
+            [],
+            [ast.keyword('a', ast.Constant(None))],
+            [],
+            [ast.Name('dataclass')],
+        )
+        self.assertEqual(ast.dump(node),
+            "ClassDef(name='T', keywords=[keyword(arg='a', value=Constant(value=None))], decorator_list=[Name(id='dataclass')])",
+        )
+        self.assertEqual(ast.dump(node, annotate_fields=False),
+            "ClassDef('T', [], [keyword('a', Constant(None))], [], [Name('dataclass')])",
+        )
+
+    def test_dump_show_empty(self):
+        def check_node(node, empty, full, **kwargs):
+            with self.subTest(show_empty=False):
+                self.assertEqual(
+                    ast.dump(node, show_empty=False, **kwargs),
+                    empty,
+                )
+            with self.subTest(show_empty=True):
+                self.assertEqual(
+                    ast.dump(node, show_empty=True, **kwargs),
+                    full,
+                )
+
+        def check_text(code, empty, full, **kwargs):
+            check_node(ast.parse(code), empty, full, **kwargs)
+
+        check_node(
+            ast.arguments(),
+            empty="arguments()",
+            full="arguments(posonlyargs=[], args=[], kwonlyargs=[], kw_defaults=[], defaults=[])",
+        )
+
+        check_node(
+            # Corner case: there are no real `Name` instances with `id=''`:
+            ast.Name(id='', ctx=ast.Load()),
+            empty="Name(id='', ctx=Load())",
+            full="Name(id='', ctx=Load())",
+        )
+
+        check_node(
+            ast.MatchSingleton(value=None),
+            empty="MatchSingleton(value=None)",
+            full="MatchSingleton(value=None)",
+        )
+
+        check_node(
+            ast.Constant(value=None),
+            empty="Constant(value=None)",
+            full="Constant(value=None)",
+        )
+
+        check_node(
+            ast.Constant(value=''),
+            empty="Constant(value='')",
+            full="Constant(value='')",
+        )
+
+        check_text(
+            "def a(b: int = 0, *, c): ...",
+            empty="Module(body=[FunctionDef(name='a', args=arguments(args=[arg(arg='b', annotation=Name(id='int', ctx=Load()))], kwonlyargs=[arg(arg='c')], kw_defaults=[None], defaults=[Constant(value=0)]), body=[Expr(value=Constant(value=Ellipsis))])])",
+            full="Module(body=[FunctionDef(name='a', args=arguments(posonlyargs=[], args=[arg(arg='b', annotation=Name(id='int', ctx=Load()))], kwonlyargs=[arg(arg='c')], kw_defaults=[None], defaults=[Constant(value=0)]), body=[Expr(value=Constant(value=Ellipsis))], decorator_list=[], type_params=[])], type_ignores=[])",
+        )
+
+        check_text(
+            "def a(b: int = 0, *, c): ...",
+            empty="Module(body=[FunctionDef(name='a', args=arguments(args=[arg(arg='b', annotation=Name(id='int', ctx=Load(), lineno=1, col_offset=9, end_lineno=1, end_col_offset=12), lineno=1, col_offset=6, end_lineno=1, end_col_offset=12)], kwonlyargs=[arg(arg='c', lineno=1, col_offset=21, end_lineno=1, end_col_offset=22)], kw_defaults=[None], defaults=[Constant(value=0, lineno=1, col_offset=15, end_lineno=1, end_col_offset=16)]), body=[Expr(value=Constant(value=Ellipsis, lineno=1, col_offset=25, end_lineno=1, end_col_offset=28), lineno=1, col_offset=25, end_lineno=1, end_col_offset=28)], lineno=1, col_offset=0, end_lineno=1, end_col_offset=28)])",
+            full="Module(body=[FunctionDef(name='a', args=arguments(posonlyargs=[], args=[arg(arg='b', annotation=Name(id='int', ctx=Load(), lineno=1, col_offset=9, end_lineno=1, end_col_offset=12), lineno=1, col_offset=6, end_lineno=1, end_col_offset=12)], kwonlyargs=[arg(arg='c', lineno=1, col_offset=21, end_lineno=1, end_col_offset=22)], kw_defaults=[None], defaults=[Constant(value=0, lineno=1, col_offset=15, end_lineno=1, end_col_offset=16)]), body=[Expr(value=Constant(value=Ellipsis, lineno=1, col_offset=25, end_lineno=1, end_col_offset=28), lineno=1, col_offset=25, end_lineno=1, end_col_offset=28)], decorator_list=[], type_params=[], lineno=1, col_offset=0, end_lineno=1, end_col_offset=28)], type_ignores=[])",
+            include_attributes=True,
+        )
+
+        check_text(
+            'spam(eggs, "and cheese")',
+            empty="Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load()), args=[Name(id='eggs', ctx=Load()), Constant(value='and cheese')]))])",
+            full="Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load()), args=[Name(id='eggs', ctx=Load()), Constant(value='and cheese')], keywords=[]))], type_ignores=[])",
+        )
+
+        check_text(
+            'spam(eggs, text="and cheese")',
+            empty="Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load()), args=[Name(id='eggs', ctx=Load())], keywords=[keyword(arg='text', value=Constant(value='and cheese'))]))])",
+            full="Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load()), args=[Name(id='eggs', ctx=Load())], keywords=[keyword(arg='text', value=Constant(value='and cheese'))]))], type_ignores=[])",
+        )
+
+        check_text(
+            "import _ast as ast; from module import sub",
+            empty="Module(body=[Import(names=[alias(name='_ast', asname='ast')]), ImportFrom(module='module', names=[alias(name='sub')], level=0)])",
+            full="Module(body=[Import(names=[alias(name='_ast', asname='ast')]), ImportFrom(module='module', names=[alias(name='sub')], level=0)], type_ignores=[])",
+        )
 
     def test_copy_location(self):
         src = ast.parse('1 + 1', mode='eval')
@@ -1361,14 +1467,13 @@ Module(
             "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', lineno=1, col_offset=6, end_lineno=1, "
-            "end_col_offset=12)], keywords=[], lineno=1, col_offset=0, end_lineno=1, "
+            "end_col_offset=12)], 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(), "
             "lineno=1, col_offset=0, end_lineno=1, end_col_offset=0), "
             "args=[Constant(value='eggs', lineno=1, col_offset=0, end_lineno=1, "
-            "end_col_offset=0)], keywords=[], lineno=1, col_offset=0, end_lineno=1, "
-            "end_col_offset=0), lineno=1, col_offset=0, end_lineno=1, end_col_offset=0)], "
-            "type_ignores=[])"
+            "end_col_offset=0)], lineno=1, col_offset=0, end_lineno=1, "
+            "end_col_offset=0), lineno=1, col_offset=0, end_lineno=1, end_col_offset=0)])"
         )
 
     def test_increment_lineno(self):
diff --git a/Misc/NEWS.d/next/Library/2024-02-28-11-51-51.gh-issue-116023.CGYhFh.rst b/Misc/NEWS.d/next/Library/2024-02-28-11-51-51.gh-issue-116023.CGYhFh.rst
new file mode 100644 (file)
index 0000000..bebb67e
--- /dev/null
@@ -0,0 +1,3 @@
+Don't show empty fields (value ``None`` or ``[]``)
+in :func:`ast.dump` by default. Add ``show_empty=False``
+parameter to optionally show them.