diff --git a/Lib/ast.py b/Lib/ast.py index e347d8b9da..9d1299cd36 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -28,6 +28,7 @@ import sys from _ast import * from contextlib import contextmanager, nullcontext from enum import IntEnum, auto +from functools import lru_cache def parse(source, filename='', mode='exec', *, @@ -101,8 +102,32 @@ def literal_eval(node_or_string): return _convert_signed_num(node) return _convert(node_or_string) - -def dump(node, annotate_fields=True, include_attributes=False, *, indent=None): +@lru_cache +def _field_defaults(node): + fields = {} + signature = node.__doc__ + if not node._fields: + return fields + for field in signature[len(node.__name__)+1:-1].split(", "): + field_type, field_name = field.split() + if field_type[-1] == "?": + default_value = None + elif field_type[-1] == "*": + default_value = [] + else: + continue + + fields[field_name] = default_value + return fields + +def dump( + node, + annotate_fields=True, + include_attributes=False, + *, + indent=None, + omit_defaults=False +): """ Return a formatted dump of the tree in node. This is mainly useful for debugging purposes. If annotate_fields is true (by default), @@ -112,7 +137,9 @@ def dump(node, annotate_fields=True, include_attributes=False, *, indent=None): numbers and column offsets are not dumped by default. If this is wanted, 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. + level. None (the default) selects the single line representation. If + omit_defaults is True, default values of nodes will be omitted from the + representation. """ def _format(node, level=0): if indent is not None: @@ -127,13 +154,18 @@ def dump(node, annotate_fields=True, include_attributes=False, *, indent=None): args = [] allsimple = True keywords = annotate_fields + field_defaults = _field_defaults(cls) for name in node._fields: try: value = getattr(node, name) except AttributeError: keywords = True continue - if value is None and getattr(cls, name, ...) is None: + if ( + omit_defaults + and name in field_defaults + and value == field_defaults[name] + ): keywords = True continue value, simple = _format(value, level) @@ -148,7 +180,11 @@ def dump(node, annotate_fields=True, include_attributes=False, *, indent=None): value = getattr(node, name) except AttributeError: continue - if value is None and getattr(cls, name, ...) is None: + if ( + omit_defaults + and value is None + and getattr(cls, name, ...) is None + ): continue value, simple = _format(value, level) allsimple = allsimple and simple