Introduce DebugVisitor.show() + tests

This commit is contained in:
Łukasz Langa 2018-03-23 17:07:20 -07:00
parent c98a6f134f
commit 7e1c5b2ba6
4 changed files with 870 additions and 1 deletions

View File

@ -320,6 +320,15 @@ def visit_default(self, node: LN) -> Iterator[T]:
out(f' {node.prefix!r}', fg='green', bold=False, nl=False) out(f' {node.prefix!r}', fg='green', bold=False, nl=False)
out(f' {node.value!r}', fg='blue', bold=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) KEYWORDS = set(keyword.kwlist)
WHITESPACE = {token.DEDENT, token.INDENT, token.NEWLINE} WHITESPACE = {token.DEDENT, token.INDENT, token.NEWLINE}

804
tests/debug_visitor.out Normal file
View File

@ -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

32
tests/debug_visitor.py Normal file
View File

@ -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)))

View File

@ -25,7 +25,7 @@ def dump_to_stderr(*output: str) -> str:
def read_data(name: str) -> Tuple[str, str]: def read_data(name: str) -> Tuple[str, str]:
"""read_data('test_name') -> 'input', 'output'""" """read_data('test_name') -> 'input', 'output'"""
if not name.endswith('.py'): if not name.endswith(('.py', '.out')):
name += '.py' name += '.py'
_input: List[str] = [] _input: List[str] = []
_output: List[str] = [] _output: List[str] = []
@ -282,6 +282,30 @@ def test_is_python36(self) -> None:
node = black.lib2to3_parse(expected) node = black.lib2to3_parse(expected)
self.assertFalse(black.is_python36(node)) 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__': if __name__ == '__main__':
unittest.main() unittest.main()