out(f' {node.prefix!r}', fg='green', bold=False, nl=False)
out(f' {node.value!r}', fg='blue', bold=False)
+ @classmethod
+ def show(cls, code: str) -> None:
+ """Pretty-prints a given string of `code`.
+
+ Convenience method for debugging.
+ """
+ v: DebugVisitor[None] = DebugVisitor()
+ list(v.visit(lib2to3_parse(code)))
+
KEYWORDS = set(keyword.kwlist)
WHITESPACE = {token.DEDENT, token.INDENT, token.NEWLINE}
--- /dev/null
+file_input
+ decorated
+ decorator
+ AT
+ '@'
+ NAME
+ 'dataclass'
+ NEWLINE
+ '\n'
+ /decorator
+ classdef
+ NAME
+ 'class'
+ NAME
+ ' '
+ 'DebugVisitor'
+ LPAR
+ '('
+ power
+ NAME
+ 'Visitor'
+ trailer
+ LSQB
+ '['
+ NAME
+ 'T'
+ RSQB
+ ']'
+ /trailer
+ /power
+ RPAR
+ ')'
+ COLON
+ ':'
+ suite
+ NEWLINE
+ '\n'
+ INDENT
+ ' '
+ simple_stmt
+ expr_stmt
+ NAME
+ 'tree_depth'
+ annassign
+ COLON
+ ':'
+ NAME
+ ' '
+ 'int'
+ EQUAL
+ ' '
+ '='
+ NUMBER
+ ' '
+ '0'
+ /annassign
+ /expr_stmt
+ NEWLINE
+ '\n'
+ /simple_stmt
+ funcdef
+ NAME
+ '\n '
+ 'def'
+ NAME
+ ' '
+ 'visit_default'
+ parameters
+ LPAR
+ '('
+ typedargslist
+ NAME
+ 'self'
+ COMMA
+ ','
+ tname
+ NAME
+ ' '
+ 'node'
+ COLON
+ ':'
+ NAME
+ ' '
+ 'LN'
+ /tname
+ /typedargslist
+ RPAR
+ ')'
+ /parameters
+ RARROW
+ ' '
+ '->'
+ power
+ NAME
+ ' '
+ 'Iterator'
+ trailer
+ LSQB
+ '['
+ NAME
+ 'T'
+ RSQB
+ ']'
+ /trailer
+ /power
+ COLON
+ ':'
+ suite
+ NEWLINE
+ '\n'
+ INDENT
+ ' '
+ simple_stmt
+ expr_stmt
+ NAME
+ 'indent'
+ EQUAL
+ ' '
+ '='
+ term
+ STRING
+ ' '
+ "' '"
+ STAR
+ ' '
+ '*'
+ atom
+ LPAR
+ ' '
+ '('
+ term
+ NUMBER
+ '2'
+ STAR
+ ' '
+ '*'
+ power
+ NAME
+ ' '
+ 'self'
+ trailer
+ DOT
+ '.'
+ NAME
+ 'tree_depth'
+ /trailer
+ /power
+ /term
+ RPAR
+ ')'
+ /atom
+ /term
+ /expr_stmt
+ NEWLINE
+ '\n'
+ /simple_stmt
+ if_stmt
+ NAME
+ ' '
+ 'if'
+ power
+ NAME
+ ' '
+ 'isinstance'
+ trailer
+ LPAR
+ '('
+ arglist
+ NAME
+ 'node'
+ COMMA
+ ','
+ NAME
+ ' '
+ 'Node'
+ /arglist
+ RPAR
+ ')'
+ /trailer
+ /power
+ COLON
+ ':'
+ suite
+ NEWLINE
+ '\n'
+ INDENT
+ ' '
+ simple_stmt
+ expr_stmt
+ NAME
+ '_type'
+ EQUAL
+ ' '
+ '='
+ power
+ NAME
+ ' '
+ 'type_repr'
+ trailer
+ LPAR
+ '('
+ power
+ NAME
+ 'node'
+ trailer
+ DOT
+ '.'
+ NAME
+ 'type'
+ /trailer
+ /power
+ RPAR
+ ')'
+ /trailer
+ /power
+ /expr_stmt
+ NEWLINE
+ '\n'
+ /simple_stmt
+ simple_stmt
+ power
+ NAME
+ ' '
+ 'out'
+ trailer
+ LPAR
+ '('
+ arglist
+ STRING
+ "f'{indent}{_type}'"
+ COMMA
+ ','
+ argument
+ NAME
+ ' '
+ 'fg'
+ EQUAL
+ '='
+ STRING
+ "'yellow'"
+ /argument
+ /arglist
+ RPAR
+ ')'
+ /trailer
+ /power
+ NEWLINE
+ '\n'
+ /simple_stmt
+ simple_stmt
+ expr_stmt
+ power
+ NAME
+ ' '
+ 'self'
+ trailer
+ DOT
+ '.'
+ NAME
+ 'tree_depth'
+ /trailer
+ /power
+ PLUSEQUAL
+ ' '
+ '+='
+ NUMBER
+ ' '
+ '1'
+ /expr_stmt
+ NEWLINE
+ '\n'
+ /simple_stmt
+ for_stmt
+ NAME
+ ' '
+ 'for'
+ NAME
+ ' '
+ 'child'
+ NAME
+ ' '
+ 'in'
+ power
+ NAME
+ ' '
+ 'node'
+ trailer
+ DOT
+ '.'
+ NAME
+ 'children'
+ /trailer
+ /power
+ COLON
+ ':'
+ suite
+ NEWLINE
+ '\n'
+ INDENT
+ ' '
+ simple_stmt
+ yield_expr
+ NAME
+ 'yield'
+ yield_arg
+ NAME
+ ' '
+ 'from'
+ power
+ NAME
+ ' '
+ 'self'
+ trailer
+ DOT
+ '.'
+ NAME
+ 'visit'
+ /trailer
+ trailer
+ LPAR
+ '('
+ NAME
+ 'child'
+ RPAR
+ ')'
+ /trailer
+ /power
+ /yield_arg
+ /yield_expr
+ NEWLINE
+ '\n'
+ /simple_stmt
+ DEDENT
+ ''
+ /suite
+ /for_stmt
+ simple_stmt
+ expr_stmt
+ power
+ NAME
+ '\n '
+ 'self'
+ trailer
+ DOT
+ '.'
+ NAME
+ 'tree_depth'
+ /trailer
+ /power
+ MINEQUAL
+ ' '
+ '-='
+ NUMBER
+ ' '
+ '1'
+ /expr_stmt
+ NEWLINE
+ '\n'
+ /simple_stmt
+ simple_stmt
+ power
+ NAME
+ ' '
+ 'out'
+ trailer
+ LPAR
+ '('
+ arglist
+ STRING
+ "f'{indent}/{_type}'"
+ COMMA
+ ','
+ argument
+ NAME
+ ' '
+ 'fg'
+ EQUAL
+ '='
+ STRING
+ "'yellow'"
+ /argument
+ COMMA
+ ','
+ argument
+ NAME
+ ' '
+ 'bold'
+ EQUAL
+ '='
+ NAME
+ 'False'
+ /argument
+ /arglist
+ RPAR
+ ')'
+ /trailer
+ /power
+ NEWLINE
+ '\n'
+ /simple_stmt
+ DEDENT
+ ''
+ /suite
+ NAME
+ ' '
+ 'else'
+ COLON
+ ':'
+ suite
+ NEWLINE
+ '\n'
+ INDENT
+ ' '
+ simple_stmt
+ expr_stmt
+ NAME
+ '_type'
+ EQUAL
+ ' '
+ '='
+ power
+ NAME
+ ' '
+ 'token'
+ trailer
+ DOT
+ '.'
+ NAME
+ 'tok_name'
+ /trailer
+ trailer
+ DOT
+ '.'
+ NAME
+ 'get'
+ /trailer
+ trailer
+ LPAR
+ '('
+ arglist
+ power
+ NAME
+ 'node'
+ trailer
+ DOT
+ '.'
+ NAME
+ 'type'
+ /trailer
+ /power
+ COMMA
+ ','
+ power
+ NAME
+ ' '
+ 'str'
+ trailer
+ LPAR
+ '('
+ power
+ NAME
+ 'node'
+ trailer
+ DOT
+ '.'
+ NAME
+ 'type'
+ /trailer
+ /power
+ RPAR
+ ')'
+ /trailer
+ /power
+ /arglist
+ RPAR
+ ')'
+ /trailer
+ /power
+ /expr_stmt
+ NEWLINE
+ '\n'
+ /simple_stmt
+ simple_stmt
+ power
+ NAME
+ ' '
+ 'out'
+ trailer
+ LPAR
+ '('
+ arglist
+ STRING
+ "f'{indent}{_type}'"
+ COMMA
+ ','
+ argument
+ NAME
+ ' '
+ 'fg'
+ EQUAL
+ '='
+ STRING
+ "'blue'"
+ /argument
+ COMMA
+ ','
+ argument
+ NAME
+ ' '
+ 'nl'
+ EQUAL
+ '='
+ NAME
+ 'False'
+ /argument
+ /arglist
+ RPAR
+ ')'
+ /trailer
+ /power
+ NEWLINE
+ '\n'
+ /simple_stmt
+ if_stmt
+ NAME
+ ' '
+ 'if'
+ power
+ NAME
+ ' '
+ 'node'
+ trailer
+ DOT
+ '.'
+ NAME
+ 'prefix'
+ /trailer
+ /power
+ COLON
+ ':'
+ suite
+ NEWLINE
+ '\n'
+ INDENT
+ ' '
+ simple_stmt
+ power
+ NAME
+ " # We don't have to handle prefixes for `Node` objects since\n # that delegates to the first child anyway.\n"
+ 'out'
+ trailer
+ LPAR
+ '('
+ arglist
+ STRING
+ "f' {node.prefix!r}'"
+ COMMA
+ ','
+ argument
+ NAME
+ ' '
+ 'fg'
+ EQUAL
+ '='
+ STRING
+ "'green'"
+ /argument
+ COMMA
+ ','
+ argument
+ NAME
+ ' '
+ 'bold'
+ EQUAL
+ '='
+ NAME
+ 'False'
+ /argument
+ COMMA
+ ','
+ argument
+ NAME
+ ' '
+ 'nl'
+ EQUAL
+ '='
+ NAME
+ 'False'
+ /argument
+ /arglist
+ RPAR
+ ')'
+ /trailer
+ /power
+ NEWLINE
+ '\n'
+ /simple_stmt
+ DEDENT
+ ''
+ /suite
+ /if_stmt
+ simple_stmt
+ power
+ NAME
+ ' '
+ 'out'
+ trailer
+ LPAR
+ '('
+ arglist
+ STRING
+ "f' {node.value!r}'"
+ COMMA
+ ','
+ argument
+ NAME
+ ' '
+ 'fg'
+ EQUAL
+ '='
+ STRING
+ "'blue'"
+ /argument
+ COMMA
+ ','
+ argument
+ NAME
+ ' '
+ 'bold'
+ EQUAL
+ '='
+ NAME
+ 'False'
+ /argument
+ /arglist
+ RPAR
+ ')'
+ /trailer
+ /power
+ NEWLINE
+ '\n'
+ /simple_stmt
+ DEDENT
+ ''
+ /suite
+ /if_stmt
+ DEDENT
+ ''
+ /suite
+ /funcdef
+ decorated
+ decorator
+ AT
+ '\n '
+ '@'
+ NAME
+ 'classmethod'
+ NEWLINE
+ '\n'
+ /decorator
+ funcdef
+ NAME
+ ' '
+ 'def'
+ NAME
+ ' '
+ 'show'
+ parameters
+ LPAR
+ '('
+ typedargslist
+ NAME
+ 'cls'
+ COMMA
+ ','
+ tname
+ NAME
+ ' '
+ 'code'
+ COLON
+ ':'
+ NAME
+ ' '
+ 'str'
+ /tname
+ /typedargslist
+ RPAR
+ ')'
+ /parameters
+ RARROW
+ ' '
+ '->'
+ NAME
+ ' '
+ 'None'
+ COLON
+ ':'
+ suite
+ NEWLINE
+ '\n'
+ INDENT
+ ' '
+ simple_stmt
+ STRING
+ '"""Pretty-prints a given string of `code`.\n\n Convenience method for debugging.\n """'
+ NEWLINE
+ '\n'
+ /simple_stmt
+ simple_stmt
+ expr_stmt
+ NAME
+ ' '
+ 'v'
+ annassign
+ COLON
+ ':'
+ power
+ NAME
+ ' '
+ 'DebugVisitor'
+ trailer
+ LSQB
+ '['
+ NAME
+ 'None'
+ RSQB
+ ']'
+ /trailer
+ /power
+ EQUAL
+ ' '
+ '='
+ power
+ NAME
+ ' '
+ 'DebugVisitor'
+ trailer
+ LPAR
+ '('
+ RPAR
+ ')'
+ /trailer
+ /power
+ /annassign
+ /expr_stmt
+ NEWLINE
+ '\n'
+ /simple_stmt
+ simple_stmt
+ power
+ NAME
+ ' '
+ 'list'
+ trailer
+ LPAR
+ '('
+ power
+ NAME
+ 'v'
+ trailer
+ DOT
+ '.'
+ NAME
+ 'visit'
+ /trailer
+ trailer
+ LPAR
+ '('
+ power
+ NAME
+ 'lib2to3_parse'
+ trailer
+ LPAR
+ '('
+ NAME
+ 'code'
+ RPAR
+ ')'
+ /trailer
+ /power
+ RPAR
+ ')'
+ /trailer
+ /power
+ RPAR
+ ')'
+ /trailer
+ /power
+ NEWLINE
+ '\n'
+ /simple_stmt
+ DEDENT
+ ''
+ /suite
+ /funcdef
+ /decorated
+ DEDENT
+ ''
+ /suite
+ /classdef
+ /decorated
+ ENDMARKER
+ ''
+/file_input
--- /dev/null
+@dataclass
+class DebugVisitor(Visitor[T]):
+ tree_depth: int = 0
+
+ def visit_default(self, node: LN) -> Iterator[T]:
+ indent = ' ' * (2 * self.tree_depth)
+ if isinstance(node, Node):
+ _type = type_repr(node.type)
+ out(f'{indent}{_type}', fg='yellow')
+ self.tree_depth += 1
+ for child in node.children:
+ yield from self.visit(child)
+
+ self.tree_depth -= 1
+ out(f'{indent}/{_type}', fg='yellow', bold=False)
+ else:
+ _type = token.tok_name.get(node.type, str(node.type))
+ out(f'{indent}{_type}', fg='blue', nl=False)
+ if node.prefix:
+ # We don't have to handle prefixes for `Node` objects since
+ # that delegates to the first child anyway.
+ out(f' {node.prefix!r}', fg='green', bold=False, nl=False)
+ out(f' {node.value!r}', fg='blue', bold=False)
+
+ @classmethod
+ def show(cls, code: str) -> None:
+ """Pretty-prints a given string of `code`.
+
+ Convenience method for debugging.
+ """
+ v: DebugVisitor[None] = DebugVisitor()
+ list(v.visit(lib2to3_parse(code)))
def read_data(name: str) -> Tuple[str, str]:
"""read_data('test_name') -> 'input', 'output'"""
- if not name.endswith('.py'):
+ if not name.endswith(('.py', '.out')):
name += '.py'
_input: List[str] = []
_output: List[str] = []
node = black.lib2to3_parse(expected)
self.assertFalse(black.is_python36(node))
+ def test_debug_visitor(self) -> None:
+ source, _ = read_data('debug_visitor.py')
+ expected, _ = read_data('debug_visitor.out')
+ out_lines = []
+ err_lines = []
+
+ def out(msg: str, **kwargs: Any) -> None:
+ out_lines.append(msg)
+
+ def err(msg: str, **kwargs: Any) -> None:
+ err_lines.append(msg)
+
+ with patch("black.out", out), patch("black.err", err):
+ black.DebugVisitor.show(source)
+ actual = '\n'.join(out_lines) + '\n'
+ log_name = ''
+ if expected != actual:
+ log_name = black.dump_to_file(*out_lines)
+ self.assertEqual(
+ expected,
+ actual,
+ f"AST print out is different. Actual version dumped to {log_name}",
+ )
+
if __name__ == '__main__':
unittest.main()