Skip to content

Commit d749179

Browse files
committed
Rewrite TreeDom __str__ and __repr__
Bump version - "the first initial release" [run test]
1 parent 1189bd3 commit d749179

File tree

8 files changed

+106
-24
lines changed

8 files changed

+106
-24
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ crate-type = ["cdylib"]
1717
members = ["treedom", "matching"]
1818

1919
[workspace.package]
20-
version = "0.1.0-beta1"
20+
version = "0.1.0"
2121
edition = "2021"
2222
readme = "README.md"
2323
license = "MIT"
@@ -28,7 +28,7 @@ authors = ["awolverp"]
2828
[profile.release]
2929
codegen-units = 1
3030
debug = false
31-
# incremental = false
31+
incremental = false
3232
lto = "fat"
3333
panic = "abort"
3434
strip = "symbols"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ print(root.serialize())
7676
```
7777

7878
# TODO List
79-
- [ ] Rewrite TreeDom `__repr__` and `__str__`
79+
- [x] Rewrite TreeDom `__repr__` and `__str__`
8080
- [ ] Add benchmarks
8181
- [ ] Add memory usage report
8282
- [ ] Add PyPI version, downloads, test coverage, and python versions badges

python/markupever/_display.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import typing
2+
3+
4+
class _DisplayCharacterToken:
5+
__slots__ = ("siblings", "children")
6+
7+
def __init__(self, siblings: bool):
8+
self.siblings = siblings
9+
self.children = False
10+
11+
def __str__(self):
12+
# match (siblings, children)
13+
# (true, true) => "│ ",
14+
# (true, false) => "├── ",
15+
# (false, true) => " ",
16+
# (false, false) => "└── ",
17+
18+
if self.children:
19+
return "│ " if self.siblings else " "
20+
21+
if self.siblings:
22+
return "├── "
23+
24+
return "└── "
25+
26+
27+
class _Indentation:
28+
__slots__ = ("tokens", "ignore_root")
29+
30+
def __init__(self, ignore_root: bool):
31+
self.tokens: typing.List[_DisplayCharacterToken] = []
32+
self.ignore_root = ignore_root
33+
34+
def indent(self, siblings: bool):
35+
length = len(self.tokens)
36+
if length > 0:
37+
self.tokens[length - 1].children = True
38+
39+
self.tokens.append(_DisplayCharacterToken(siblings))
40+
return self
41+
42+
def deindent(self):
43+
self.tokens.pop()
44+
return self
45+
46+
def __str__(self) -> str:
47+
s = ""
48+
for token in self.tokens[int(self.ignore_root) :]:
49+
s += str(token)
50+
51+
return s

python/markupever/dom.py

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,32 @@ def __len__(self) -> int:
6060
return len(self._raw)
6161

6262
def __str__(self) -> str:
63-
return str(self._raw)
63+
from ._display import _Indentation
6464

65-
def __repr__(self) -> str:
66-
return repr(self._raw)
65+
res = ""
66+
67+
indent = _Indentation(True)
68+
69+
for edge in self.root().traverse():
70+
if edge.closed:
71+
if edge.node.has_children:
72+
indent.deindent()
73+
74+
continue
75+
76+
if edge.node.has_children:
77+
indent.indent(edge.node.next_sibling is not None)
78+
res += str(indent) + str(edge.node) + "\n"
79+
80+
else:
81+
indent.indent(edge.node.next_sibling is not None)
82+
res += str(indent) + str(edge.node) + "\n"
83+
indent.deindent()
84+
85+
return res[:-1] # remove the last '\n'
86+
87+
def __repr__(self):
88+
return f"TreeDom(len={len(self)}, namespaces={self.namespaces()})"
6789

6890

6991
class _ConfigNode:
@@ -550,10 +572,11 @@ def __gt__(self, value): # pragma: no cover
550572

551573
class AttrsList:
552574
"""
553-
This type is only designed for communicating with element attributes.
554-
575+
This type is only designed for communicating with element attributes.
576+
555577
Really it's a list, but has a behaviour between dictionary and list to provide you easy-to-use management.
556578
"""
579+
557580
__slots__ = ("__raw",)
558581

559582
def __init__(self, attrs: _rustlib.AttrsList):
@@ -729,14 +752,10 @@ def __setitem__(
729752
self.__raw.update_item(index, val[0], val[1])
730753

731754
@typing.overload
732-
def __getitem__(
733-
self, index: typing.Union[str, _rustlib.QualName]
734-
) -> str: ...
755+
def __getitem__(self, index: typing.Union[str, _rustlib.QualName]) -> str: ...
735756

736757
@typing.overload
737-
def __getitem__(
738-
self, index: int
739-
) -> typing.Tuple[_rustlib.QualName, str]: ...
758+
def __getitem__(self, index: int) -> typing.Tuple[_rustlib.QualName, str]: ...
740759

741760
def __getitem__(self, index):
742761
if not isinstance(index, int):
@@ -889,6 +908,7 @@ class ProcessingInstruction(BaseNode):
889908
a Node which embeds an instruction targeting a specific application but that can
890909
be ignored by any other applications which don't recognize the instruction.
891910
"""
911+
892912
_CONFIG = _ConfigNode(_rustlib.ProcessingInstruction, (Ordering.APPEND, Ordering.PREPEND))
893913

894914
@property

python/tests/test_dom.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ def test_basenode_init():
3434
_test_rustlib_node_convert(_rustlib.Doctype, markupever.dom.Doctype, dom, "name", "", "")
3535
_test_rustlib_node_convert(_rustlib.Comment, markupever.dom.Comment, dom, "content")
3636
_test_rustlib_node_convert(_rustlib.Text, markupever.dom.Text, dom, "content")
37-
_test_rustlib_node_convert(_rustlib.Element, markupever.dom.Element, dom, "name", [], False, False)
37+
_test_rustlib_node_convert(
38+
_rustlib.Element, markupever.dom.Element, dom, "name", [], False, False
39+
)
3840
_test_rustlib_node_convert(
3941
_rustlib.ProcessingInstruction, markupever.dom.ProcessingInstruction, dom, "name", "data"
4042
)
@@ -55,7 +57,9 @@ def test_connect_node():
5557
dom = markupever.dom.TreeDom()
5658
root = dom.root()
5759

58-
html = root.create_element(markupever.dom.QualName("html", "html"), {"lang": "en"}, False, False)
60+
html = root.create_element(
61+
markupever.dom.QualName("html", "html"), {"lang": "en"}, False, False
62+
)
5963
assert isinstance(html, markupever.dom.Element)
6064
assert html.parent == root
6165
assert html.name == "html"

python/tests/test_parser.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ def test_parser(): # this is a copy of test_rustlib.test_parser for markupever.
5656

5757

5858
def test_parse_function():
59-
assert isinstance(markupever.parse("<html></html>", markupever.XmlOptions()), markupever.dom.TreeDom)
59+
assert isinstance(
60+
markupever.parse("<html></html>", markupever.XmlOptions()), markupever.dom.TreeDom
61+
)
6062

6163

6264
def test_parse_file_function(tmp_path):
@@ -78,6 +80,8 @@ def test_parse_file_function(tmp_path):
7880
markupever.parse_file(str(file), markupever.HtmlOptions())
7981

8082
file.write_bytes(b"<body></body>")
81-
assert isinstance(markupever.parse_file(str(file), markupever.HtmlOptions()), markupever.dom.TreeDom)
83+
assert isinstance(
84+
markupever.parse_file(str(file), markupever.HtmlOptions()), markupever.dom.TreeDom
85+
)
8286

8387
markupever.parse_file(file, markupever.HtmlOptions())

src/nodes.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,12 @@ impl PyText {
710710
}
711711
}
712712

713-
#[pyo3::pyclass(name = "AttrsListItems", module = "markupever._rustlib", mapping, frozen)]
713+
#[pyo3::pyclass(
714+
name = "AttrsListItems",
715+
module = "markupever._rustlib",
716+
mapping,
717+
frozen
718+
)]
714719
pub struct PyAttrsListItems {
715720
guard: NodeGuard,
716721
index: std::sync::atomic::AtomicUsize,

treedom/src/dom.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ impl<'a> Serializer<'a> {
9696

9797
fn serialize_iter<S>(
9898
&self,
99-
iter: impl Iterator<Item=ego_tree::iter::Edge<'a, interface::Interface>>,
99+
iter: impl Iterator<Item = ego_tree::iter::Edge<'a, interface::Interface>>,
100100
serializer: &mut S,
101101
) -> std::io::Result<()>
102102
where
@@ -145,11 +145,9 @@ impl<'a> Serializer<'a> {
145145
}
146146
}
147147

148-
fn skip_last<T>(mut iter: impl Iterator<Item=T>) -> impl Iterator<Item=T> {
148+
fn skip_last<T>(mut iter: impl Iterator<Item = T>) -> impl Iterator<Item = T> {
149149
let last = iter.next();
150-
iter.scan(last, |state, item| {
151-
std::mem::replace(state, Some(item))
152-
})
150+
iter.scan(last, |state, item| std::mem::replace(state, Some(item)))
153151
}
154152

155153
impl markup5ever::serialize::Serialize for Serializer<'_> {

0 commit comments

Comments
 (0)