Skip to content

Commit 00a28a6

Browse files
👌 IMPROVE: Use regular __init__ to create SyntaxTreeNodes (#132)
Co-authored-by: Chris Sewell <chrisj_sewell@hotmail.com>
1 parent ab287be commit 00a28a6

File tree

3 files changed

+47
-49
lines changed

3 files changed

+47
-49
lines changed

docs/using.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ Here's some text and an image ![title](image.png)
265265
> a *quote*
266266
""")
267267
268-
node = SyntaxTreeNode.from_tokens(tokens)
268+
node = SyntaxTreeNode(tokens)
269269
print(node.pretty(indent=2, show_text=True))
270270
```
271271

markdown_it/tree.py

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
Optional,
1313
Any,
1414
TypeVar,
15-
Type,
1615
overload,
1716
Union,
1817
)
@@ -33,8 +32,7 @@ class SyntaxTreeNode:
3332
"""A Markdown syntax tree node.
3433
3534
A class that can be used to construct a tree representation of a linear
36-
`markdown-it-py` token stream. Use `SyntaxTreeNode.from_tokens` to
37-
initialize instead of the `__init__` method.
35+
`markdown-it-py` token stream.
3836
3937
Each node in the tree represents either:
4038
- root of the Markdown document
@@ -43,10 +41,12 @@ class SyntaxTreeNode:
4341
between
4442
"""
4543

46-
def __init__(self) -> None:
47-
"""Initialize a root node with no children.
44+
def __init__(
45+
self, tokens: Sequence[Token] = (), *, create_root: bool = True
46+
) -> None:
47+
"""Initialize a `SyntaxTreeNode` from a token stream.
4848
49-
You probably need `SyntaxTreeNode.from_tokens` instead.
49+
If `create_root` is True, create a root node for the document.
5050
"""
5151
# Only nodes representing an unnested token have self.token
5252
self.token: Optional[Token] = None
@@ -61,6 +61,28 @@ def __init__(self) -> None:
6161
# children (i.e. inline or img)
6262
self._children: list = []
6363

64+
if create_root:
65+
self._set_children_from_tokens(tokens)
66+
return
67+
68+
if not tokens:
69+
raise ValueError(
70+
"Can only create root from empty token sequence."
71+
" Set `create_root=True`."
72+
)
73+
elif len(tokens) == 1:
74+
inline_token = tokens[0]
75+
if inline_token.nesting:
76+
raise ValueError(
77+
"Unequal nesting level at the start and end of token stream."
78+
)
79+
self.token = inline_token
80+
if inline_token.children:
81+
self._set_children_from_tokens(inline_token.children)
82+
else:
83+
self.nester_tokens = _NesterTokens(tokens[0], tokens[-1])
84+
self._set_children_from_tokens(tokens[1:-1])
85+
6486
def __repr__(self) -> str:
6587
return f"{type(self).__name__}({self.type})"
6688

@@ -77,16 +99,6 @@ def __getitem__(
7799
) -> Union[_NodeType, List[_NodeType]]:
78100
return self.children[item]
79101

80-
@classmethod
81-
def from_tokens(cls: Type[_NodeType], tokens: Sequence[Token]) -> _NodeType:
82-
"""Instantiate a `SyntaxTreeNode` from a token stream.
83-
84-
This is the standard method for instantiating `SyntaxTreeNode`.
85-
"""
86-
root = cls()
87-
root._set_children_from_tokens(tokens)
88-
return root
89-
90102
def to_tokens(self: _NodeType) -> List[Token]:
91103
"""Recover the linear token stream."""
92104

@@ -186,23 +198,14 @@ def previous_sibling(self: _NodeType) -> Optional[_NodeType]:
186198
return self.siblings[self_index - 1]
187199
return None
188200

189-
def _make_child(
190-
self: _NodeType,
191-
*,
192-
token: Optional[Token] = None,
193-
nester_tokens: Optional[_NesterTokens] = None,
194-
) -> _NodeType:
195-
"""Make and return a child node for `self`."""
196-
if token and nester_tokens or not token and not nester_tokens:
197-
raise ValueError("must specify either `token` or `nester_tokens`")
198-
child = type(self)()
199-
if token:
200-
child.token = token
201-
else:
202-
child.nester_tokens = nester_tokens
201+
def _add_child(
202+
self,
203+
tokens: Sequence[Token],
204+
) -> None:
205+
"""Make a child node for `self`."""
206+
child = type(self)(tokens, create_root=False)
203207
child.parent = self
204208
self.children.append(child)
205-
return child
206209

207210
def _set_children_from_tokens(self, tokens: Sequence[Token]) -> None:
208211
"""Convert the token stream to a tree structure and set the resulting
@@ -211,27 +214,22 @@ def _set_children_from_tokens(self, tokens: Sequence[Token]) -> None:
211214
while reversed_tokens:
212215
token = reversed_tokens.pop()
213216

214-
if token.nesting == 0:
215-
child = self._make_child(token=token)
216-
if token.children:
217-
child._set_children_from_tokens(token.children)
217+
if not token.nesting:
218+
self._add_child([token])
218219
continue
219-
220-
assert token.nesting == 1
220+
if token.nesting != 1:
221+
raise ValueError("Invalid token nesting")
221222

222223
nested_tokens = [token]
223224
nesting = 1
224-
while reversed_tokens and nesting != 0:
225+
while reversed_tokens and nesting:
225226
token = reversed_tokens.pop()
226227
nested_tokens.append(token)
227228
nesting += token.nesting
228-
if nesting != 0:
229+
if nesting:
229230
raise ValueError(f"unclosed tokens starting {nested_tokens[0]}")
230231

231-
child = self._make_child(
232-
nester_tokens=_NesterTokens(nested_tokens[0], nested_tokens[-1])
233-
)
234-
child._set_children_from_tokens(nested_tokens[1:-1])
232+
self._add_child(nested_tokens)
235233

236234
def pretty(
237235
self, *, indent: int = 2, show_text: bool = False, _current: int = 0

tests/test_tree.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@
1010

1111
def test_tree_to_tokens_conversion():
1212
tokens = MarkdownIt().parse(EXAMPLE_MARKDOWN)
13-
tokens_after_roundtrip = SyntaxTreeNode.from_tokens(tokens).to_tokens()
13+
tokens_after_roundtrip = SyntaxTreeNode(tokens).to_tokens()
1414
assert tokens == tokens_after_roundtrip
1515

1616

1717
def test_property_passthrough():
1818
tokens = MarkdownIt().parse(EXAMPLE_MARKDOWN)
1919
heading_open = tokens[0]
20-
tree = SyntaxTreeNode.from_tokens(tokens)
20+
tree = SyntaxTreeNode(tokens)
2121
heading_node = tree.children[0]
2222
assert heading_open.tag == heading_node.tag
2323
assert tuple(heading_open.map) == heading_node.map
@@ -32,7 +32,7 @@ def test_property_passthrough():
3232

3333
def test_type():
3434
tokens = MarkdownIt().parse(EXAMPLE_MARKDOWN)
35-
tree = SyntaxTreeNode.from_tokens(tokens)
35+
tree = SyntaxTreeNode(tokens)
3636
# Root type is "root"
3737
assert tree.type == "root"
3838
# "_open" suffix must be stripped from nested token type
@@ -44,7 +44,7 @@ def test_type():
4444

4545
def test_sibling_traverse():
4646
tokens = MarkdownIt().parse(EXAMPLE_MARKDOWN)
47-
tree = SyntaxTreeNode.from_tokens(tokens)
47+
tree = SyntaxTreeNode(tokens)
4848
paragraph_inline_node = tree.children[1].children[0]
4949
text_node = paragraph_inline_node.children[0]
5050
assert text_node.type == "text"
@@ -70,5 +70,5 @@ def test_pretty(file_regression):
7070
> a *quote*
7171
"""
7272
)
73-
node = SyntaxTreeNode.from_tokens(tokens)
73+
node = SyntaxTreeNode(tokens)
7474
file_regression.check(node.pretty(indent=2, show_text=True), extension=".xml")

0 commit comments

Comments
 (0)