mirror of
https://github.com/davidhalter/parso.git
synced 2026-02-09 19:31:24 +08:00
Add NodeOrLeaf.dump() and NodeOrLeaf.search_ancestor() (#187)
- Add `NodeOrLeaf.dump()` to generate a readable and "round-trippable" dump for a parser tree - `parso.tree.search_ancestor()` is deprecated, use `NodeOrLeaf.search_ancestor()` instead - Set up children's parent in `BaseNode.__init__()` - Add test for `search_ancestor` - Various small type annotations improvements
This commit is contained in:
182
test/test_dump_tree.py
Normal file
182
test/test_dump_tree.py
Normal file
@@ -0,0 +1,182 @@
|
||||
from textwrap import dedent
|
||||
|
||||
import pytest
|
||||
|
||||
from parso import parse
|
||||
# Using star import for easier eval testing below.
|
||||
from parso.python.tree import * # noqa: F403
|
||||
from parso.tree import * # noqa: F403
|
||||
from parso.tree import ErrorLeaf, TypedLeaf
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'indent,expected_dump', [
|
||||
(None, "Module(["
|
||||
"Lambda(["
|
||||
"Keyword('lambda', (1, 0)), "
|
||||
"Param(["
|
||||
"Name('x', (1, 7), prefix=' '), "
|
||||
"Operator(',', (1, 8)), "
|
||||
"]), "
|
||||
"Param(["
|
||||
"Name('y', (1, 10), prefix=' '), "
|
||||
"]), "
|
||||
"Operator(':', (1, 11)), "
|
||||
"PythonNode('arith_expr', ["
|
||||
"Name('x', (1, 13), prefix=' '), "
|
||||
"Operator('+', (1, 15), prefix=' '), "
|
||||
"Name('y', (1, 17), prefix=' '), "
|
||||
"]), "
|
||||
"]), "
|
||||
"EndMarker('', (1, 18)), "
|
||||
"])"),
|
||||
(0, dedent('''\
|
||||
Module([
|
||||
Lambda([
|
||||
Keyword('lambda', (1, 0)),
|
||||
Param([
|
||||
Name('x', (1, 7), prefix=' '),
|
||||
Operator(',', (1, 8)),
|
||||
]),
|
||||
Param([
|
||||
Name('y', (1, 10), prefix=' '),
|
||||
]),
|
||||
Operator(':', (1, 11)),
|
||||
PythonNode('arith_expr', [
|
||||
Name('x', (1, 13), prefix=' '),
|
||||
Operator('+', (1, 15), prefix=' '),
|
||||
Name('y', (1, 17), prefix=' '),
|
||||
]),
|
||||
]),
|
||||
EndMarker('', (1, 18)),
|
||||
])''')),
|
||||
(4, dedent('''\
|
||||
Module([
|
||||
Lambda([
|
||||
Keyword('lambda', (1, 0)),
|
||||
Param([
|
||||
Name('x', (1, 7), prefix=' '),
|
||||
Operator(',', (1, 8)),
|
||||
]),
|
||||
Param([
|
||||
Name('y', (1, 10), prefix=' '),
|
||||
]),
|
||||
Operator(':', (1, 11)),
|
||||
PythonNode('arith_expr', [
|
||||
Name('x', (1, 13), prefix=' '),
|
||||
Operator('+', (1, 15), prefix=' '),
|
||||
Name('y', (1, 17), prefix=' '),
|
||||
]),
|
||||
]),
|
||||
EndMarker('', (1, 18)),
|
||||
])''')),
|
||||
('\t', dedent('''\
|
||||
Module([
|
||||
\tLambda([
|
||||
\t\tKeyword('lambda', (1, 0)),
|
||||
\t\tParam([
|
||||
\t\t\tName('x', (1, 7), prefix=' '),
|
||||
\t\t\tOperator(',', (1, 8)),
|
||||
\t\t]),
|
||||
\t\tParam([
|
||||
\t\t\tName('y', (1, 10), prefix=' '),
|
||||
\t\t]),
|
||||
\t\tOperator(':', (1, 11)),
|
||||
\t\tPythonNode('arith_expr', [
|
||||
\t\t\tName('x', (1, 13), prefix=' '),
|
||||
\t\t\tOperator('+', (1, 15), prefix=' '),
|
||||
\t\t\tName('y', (1, 17), prefix=' '),
|
||||
\t\t]),
|
||||
\t]),
|
||||
\tEndMarker('', (1, 18)),
|
||||
])''')),
|
||||
]
|
||||
)
|
||||
def test_dump_parser_tree(indent, expected_dump):
|
||||
code = "lambda x, y: x + y"
|
||||
module = parse(code)
|
||||
assert module.dump(indent=indent) == expected_dump
|
||||
|
||||
# Check that dumped tree can be eval'd to recover the parser tree and original code.
|
||||
recovered_code = eval(expected_dump).get_code()
|
||||
assert recovered_code == code
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'node,expected_dump,expected_code', [
|
||||
( # Dump intermediate node (not top level module)
|
||||
parse("def foo(x, y): return x + y").children[0], dedent('''\
|
||||
Function([
|
||||
Keyword('def', (1, 0)),
|
||||
Name('foo', (1, 4), prefix=' '),
|
||||
PythonNode('parameters', [
|
||||
Operator('(', (1, 7)),
|
||||
Param([
|
||||
Name('x', (1, 8)),
|
||||
Operator(',', (1, 9)),
|
||||
]),
|
||||
Param([
|
||||
Name('y', (1, 11), prefix=' '),
|
||||
]),
|
||||
Operator(')', (1, 12)),
|
||||
]),
|
||||
Operator(':', (1, 13)),
|
||||
ReturnStmt([
|
||||
Keyword('return', (1, 15), prefix=' '),
|
||||
PythonNode('arith_expr', [
|
||||
Name('x', (1, 22), prefix=' '),
|
||||
Operator('+', (1, 24), prefix=' '),
|
||||
Name('y', (1, 26), prefix=' '),
|
||||
]),
|
||||
]),
|
||||
])'''),
|
||||
"def foo(x, y): return x + y",
|
||||
),
|
||||
( # Dump leaf
|
||||
parse("def foo(x, y): return x + y").children[0].children[0],
|
||||
"Keyword('def', (1, 0))",
|
||||
'def',
|
||||
),
|
||||
( # Dump ErrorLeaf
|
||||
ErrorLeaf('error_type', 'error_code', (1, 1), prefix=' '),
|
||||
"ErrorLeaf('error_type', 'error_code', (1, 1), prefix=' ')",
|
||||
' error_code',
|
||||
),
|
||||
( # Dump TypedLeaf
|
||||
TypedLeaf('type', 'value', (1, 1)),
|
||||
"TypedLeaf('type', 'value', (1, 1))",
|
||||
'value',
|
||||
),
|
||||
]
|
||||
)
|
||||
def test_dump_parser_tree_not_top_level_module(node, expected_dump, expected_code):
|
||||
dump_result = node.dump()
|
||||
assert dump_result == expected_dump
|
||||
|
||||
# Check that dumped tree can be eval'd to recover the parser tree and original code.
|
||||
recovered_code = eval(dump_result).get_code()
|
||||
assert recovered_code == expected_code
|
||||
|
||||
|
||||
def test_dump_parser_tree_invalid_args():
|
||||
module = parse("lambda x, y: x + y")
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
module.dump(indent=1.1)
|
||||
|
||||
|
||||
def test_eval_dump_recovers_parent():
|
||||
module = parse("lambda x, y: x + y")
|
||||
module2 = eval(module.dump())
|
||||
assert module2.parent is None
|
||||
lambda_node = module2.children[0]
|
||||
assert lambda_node.parent is module2
|
||||
assert module2.children[1].parent is module2
|
||||
assert lambda_node.children[0].parent is lambda_node
|
||||
param_node = lambda_node.children[1]
|
||||
assert param_node.parent is lambda_node
|
||||
assert param_node.children[0].parent is param_node
|
||||
assert param_node.children[1].parent is param_node
|
||||
arith_expr_node = lambda_node.children[-1]
|
||||
assert arith_expr_node.parent is lambda_node
|
||||
assert arith_expr_node.children[0].parent is arith_expr_node
|
||||
@@ -6,6 +6,7 @@ import pytest
|
||||
|
||||
from parso import parse
|
||||
from parso.python import tree
|
||||
from parso.tree import search_ancestor
|
||||
|
||||
|
||||
class TestsFunctionAndLambdaParsing:
|
||||
@@ -239,3 +240,27 @@ def test_with_stmt_get_test_node_from_name():
|
||||
for name in with_stmt.get_defined_names(include_setitem=True)
|
||||
]
|
||||
assert tests == ["A", "B", "C", "D"]
|
||||
|
||||
|
||||
sample_module = parse('x + y')
|
||||
sample_node = sample_module.children[0]
|
||||
sample_leaf = sample_node.children[0]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'node,node_types,expected_ancestor', [
|
||||
(sample_module, ('file_input',), None),
|
||||
(sample_node, ('arith_expr',), None),
|
||||
(sample_node, ('file_input', 'eval_input'), sample_module),
|
||||
(sample_leaf, ('name',), None),
|
||||
(sample_leaf, ('arith_expr',), sample_node),
|
||||
(sample_leaf, ('file_input',), sample_module),
|
||||
(sample_leaf, ('file_input', 'arith_expr'), sample_node),
|
||||
(sample_leaf, ('shift_expr',), None),
|
||||
(sample_leaf, ('name', 'shift_expr',), None),
|
||||
(sample_leaf, (), None),
|
||||
]
|
||||
)
|
||||
def test_search_ancestor(node, node_types, expected_ancestor):
|
||||
assert node.search_ancestor(*node_types) is expected_ancestor
|
||||
assert search_ancestor(node, *node_types) is expected_ancestor # deprecated
|
||||
|
||||
Reference in New Issue
Block a user