From 7e1c5b2ba6355f1a6916cf5545dc577cf2e6c727 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C5=81ukasz=20Langa?= Date: Fri, 23 Mar 2018 17:07:20 -0700 Subject: [PATCH 1/1] Introduce DebugVisitor.show() + tests --- black.py | 9 + tests/debug_visitor.out | 804 ++++++++++++++++++++++++++++++++++++++++ tests/debug_visitor.py | 32 ++ tests/test_black.py | 26 +- 4 files changed, 870 insertions(+), 1 deletion(-) create mode 100644 tests/debug_visitor.out create mode 100644 tests/debug_visitor.py diff --git a/black.py b/black.py index f03c4f1..53d332a 100644 --- a/black.py +++ b/black.py @@ -320,6 +320,15 @@ class DebugVisitor(Visitor[T]): 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} diff --git a/tests/debug_visitor.out b/tests/debug_visitor.out new file mode 100644 index 0000000..92b7280 --- /dev/null +++ b/tests/debug_visitor.out @@ -0,0 +1,804 @@ +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 diff --git a/tests/debug_visitor.py b/tests/debug_visitor.py new file mode 100644 index 0000000..d1d1ba1 --- /dev/null +++ b/tests/debug_visitor.py @@ -0,0 +1,32 @@ +@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))) diff --git a/tests/test_black.py b/tests/test_black.py index 3415549..e512832 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -25,7 +25,7 @@ def dump_to_stderr(*output: str) -> str: 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] = [] @@ -282,6 +282,30 @@ class BlackTestCase(unittest.TestCase): 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() -- 2.39.5