Mailing List Archive

gh-116023: Add `show_empty=False` to `ast.dump` (#116037)
https://github.com/python/cpython/commit/692e902c742f577f9fc8ed81e60ed9dd6c994e1e
commit: 692e902c742f577f9fc8ed81e60ed9dd6c994e1e
branch: main
author: Nikita Sobolev <mail@sobolevn.me>
committer: sobolevn <mail@sobolevn.me>
date: 2024-04-24T11:02:38+03:00
summary:

gh-116023: Add `show_empty=False` to `ast.dump` (#116037)

Co-authored-by: Carl Meyer <carl@oddbird.net>

files:
A Misc/NEWS.d/next/Library/2024-02-28-11-51-51.gh-issue-116023.CGYhFh.rst
M Doc/library/ast.rst
M Lib/ast.py
M Lib/test/test_ast.py

diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst
index 8d407321092eef..09f2a404786fb6 100644
--- a/Doc/library/ast.rst
+++ b/Doc/library/ast.rst
@@ -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:

diff --git a/Lib/ast.py b/Lib/ast.py
index b8c4ce6f919e6b..9f386051659e76 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -114,7 +114,11 @@ def _convert(node):
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 _format(node, level=0):
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 _format(node, level=0):
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:
diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py
index 5b47cdaafb092e..44bcb9bae1cfde 100644
--- a/Lib/test/test_ast.py
+++ b/Lib/test/test_ast.py
@@ -1227,21 +1227,20 @@ 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')], "
- "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 @@ def test_dump_indent(self):
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 @@ 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')],
-\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 @@ def test_dump_indent(self):
col_offset=11,
end_lineno=1,
end_col_offset=23)],
- keywords=[],
lineno=1,
col_offset=0,
end_lineno=1,
@@ -1302,8 +1296,7 @@ def test_dump_indent(self):
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 @@ def test_dump_incomplete(self):
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 @@ def test_fix_missing_locations(self):
"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
index 00000000000000..bebb67e585eea6
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-02-28-11-51-51.gh-issue-116023.CGYhFh.rst
@@ -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.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-leave@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: list-python-checkins@lists.gossamer-threads.com