1 """
2 Copy-parse of ast.dump, removing the `isinstance` checks. This is needed,
3 because testing pegen requires generating a C extension module, which contains
4 a copy of the symbols defined in Python-ast.c. Thus, the isinstance check would
5 always fail. We rely on string comparison of the base classes instead.
6 TODO: Remove the above-described hack.
7 """
8
9 from typing import Any, Optional, Tuple
10
11
12 def ast_dump(
13 node: Any,
14 annotate_fields: bool = True,
15 include_attributes: bool = False,
16 *,
17 indent: Optional[str] = None,
18 ) -> str:
19 def _format(node: Any, level: int = 0) -> Tuple[str, bool]:
20 if indent is not None:
21 level += 1
22 prefix = "\n" + indent * level
23 sep = ",\n" + indent * level
24 else:
25 prefix = ""
26 sep = ", "
27 if any(cls.__name__ == "AST" for cls in node.__class__.__mro__):
28 cls = type(node)
29 args = []
30 allsimple = True
31 keywords = annotate_fields
32 for name in node._fields:
33 try:
34 value = getattr(node, name)
35 except AttributeError:
36 keywords = True
37 continue
38 if value is None and getattr(cls, name, ...) is None:
39 keywords = True
40 continue
41 value, simple = _format(value, level)
42 allsimple = allsimple and simple
43 if keywords:
44 args.append("%s=%s" % (name, value))
45 else:
46 args.append(value)
47 if include_attributes and node._attributes:
48 for name in node._attributes:
49 try:
50 value = getattr(node, name)
51 except AttributeError:
52 continue
53 if value is None and getattr(cls, name, ...) is None:
54 continue
55 value, simple = _format(value, level)
56 allsimple = allsimple and simple
57 args.append("%s=%s" % (name, value))
58 if allsimple and len(args) <= 3:
59 return "%s(%s)" % (node.__class__.__name__, ", ".join(args)), not args
60 return "%s(%s%s)" % (node.__class__.__name__, prefix, sep.join(args)), False
61 elif isinstance(node, list):
62 if not node:
63 return "[]", True
64 return "[%s%s]" % (prefix, sep.join(_format(x, level)[0] for x in node)), False
65 return repr(node), True
66
67 if all(cls.__name__ != "AST" for cls in node.__class__.__mro__):
68 raise TypeError("expected AST, got %r" % node.__class__.__name__)
69 if indent is not None and not isinstance(indent, str):
70 indent = " " * indent
71 return _format(node)[0]