Skip to content

Commit

Permalink
Merge pull request #235 from c0fec0de/191-different-behaviour-between…
Browse files Browse the repository at this point in the history
…-rendertreeroot-and-rendertreerootby_attrlambda-x-x

191 different behaviour between rendertreeroot and rendertreerootby attrlambda x x
  • Loading branch information
c0fec0de committed Oct 11, 2023
2 parents b671357 + 9d90af8 commit e1e0265
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 118 deletions.
37 changes: 26 additions & 11 deletions anytree/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,14 @@ def __item(node, continues, style):
return Row(pre, fill, node)

def __str__(self):
lines = ["%s%r" % (pre, node) for pre, _, node in self]
return "\n".join(lines)
def get():
for row in self:
lines = repr(row.node).splitlines() or [""]
yield "%s%s" % (row.pre, lines[0])
for line in lines[1:]:
yield "%s%s" % (row.fill, line)

return "\n".join(get())

def __repr__(self):
classname = self.__class__.__name__
Expand Down Expand Up @@ -333,19 +339,28 @@ def by_attr(self, attrname="name"):
"""

def get():
for pre, fill, node in self:
attr = attrname(node) if callable(attrname) else getattr(node, attrname, "")
if isinstance(attr, (list, tuple)):
lines = attr
else:
lines = str(attr).split("\n")
yield "%s%s" % (pre, lines[0])
for line in lines[1:]:
yield "%s%s" % (fill, line)
if callable(attrname):
for row in self:
attr = attrname(row.node)
yield from _format_row_any(row, attr)
else:
for row in self:
attr = getattr(row.node, attrname, "")
yield from _format_row_any(row, attr)

return "\n".join(get())


def _format_row_any(row, attr):
if isinstance(attr, (list, tuple)):
lines = attr or [""]
else:
lines = str(attr).splitlines() or [""]
yield "%s%s" % (row.pre, lines[0])
for line in lines[1:]:
yield "%s%s" % (row.fill, line)


def _is_last(iterable):
iter_ = iter(iterable)
try:
Expand Down
8 changes: 0 additions & 8 deletions tests/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,6 @@ def assert_raises(exccls, msg):
eq_(str(exc), msg)


def eq_str(value, expected):
"""Python 2.x and 3.x compatible string compare."""
if six.PY2:
eq_(value.decode("utf-8"), expected)
else:
eq_(value, expected)


def with_setup(setup=None, teardown=None):
def decorate(func, setup=setup, teardown=teardown):
if setup:
Expand Down
52 changes: 23 additions & 29 deletions tests/test_dictimporter.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from copy import deepcopy

from helper import eq_, eq_str
from helper import eq_

from anytree import Node, RenderTree
from anytree.exporter import DictExporter
Expand All @@ -27,20 +27,17 @@ def test_dict_importer():
eq_(data, refdata)
eq_(exporter.export(root), data)
r = RenderTree(root)
expected = "\n".join(
[
"AnyNode(id='root')",
"├── AnyNode(id='sub0')",
"│ ├── AnyNode(id='sub0B')",
"│ └── AnyNode(id='sub0A')",
"└── AnyNode(id='sub1')",
" ├── AnyNode(id='sub1A')",
" ├── AnyNode(id='sub1B')",
" └── AnyNode(id='sub1C')",
" └── AnyNode(id='sub1Ca')",
]
)
eq_str(str(r), expected)
assert str(r).splitlines() == [
"AnyNode(id='root')",
"├── AnyNode(id='sub0')",
"│ ├── AnyNode(id='sub0B')",
"│ └── AnyNode(id='sub0A')",
"└── AnyNode(id='sub1')",
" ├── AnyNode(id='sub1A')",
" ├── AnyNode(id='sub1B')",
" └── AnyNode(id='sub1C')",
" └── AnyNode(id='sub1Ca')",
]


def test_dict_importer_node():
Expand All @@ -66,17 +63,14 @@ def test_dict_importer_node():
eq_(data, refdata)
eq_(exporter.export(root), data)
r = RenderTree(root)
expected = "\n".join(
[
"Node('/root')",
"├── Node('/root/sub0')",
"│ ├── Node('/root/sub0/sub0B')",
"│ └── Node('/root/sub0/sub0A')",
"└── Node('/root/sub1')",
" ├── Node('/root/sub1/sub1A')",
" ├── Node('/root/sub1/sub1B')",
" └── Node('/root/sub1/sub1C')",
" └── Node('/root/sub1/sub1C/sub1Ca')",
]
)
eq_str(str(r), expected)
assert str(r).splitlines() == [
"Node('/root')",
"├── Node('/root/sub0')",
"│ ├── Node('/root/sub0/sub0B')",
"│ └── Node('/root/sub0/sub0A')",
"└── Node('/root/sub1')",
" ├── Node('/root/sub1/sub1A')",
" ├── Node('/root/sub1/sub1B')",
" └── Node('/root/sub1/sub1C')",
" └── Node('/root/sub1/sub1C/sub1Ca')",
]
4 changes: 2 additions & 2 deletions tests/test_jsonexporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def test_json_exporter():
AnyNode(id="sub1Ca", parent=s1c)

exporter = JsonExporter(indent=2, sort_keys=True)
exported = exporter.export(root).split("\n")
exported = exporter.export(root).splitlines()
exported = [e.rstrip() for e in exported] # just a fix for a strange py2x behavior.
lines = [
"{",
Expand Down Expand Up @@ -63,7 +63,7 @@ def test_json_exporter():
eq_(exported, lines)

exporter = JsonExporter(indent=2, sort_keys=True, maxlevel=2)
exported = exporter.export(root).split("\n")
exported = exporter.export(root).splitlines()
exported = [e.rstrip() for e in exported] # just a fix for a strange py2x behavior.
limitedlines = [
"{",
Expand Down
20 changes: 7 additions & 13 deletions tests/test_node_sep.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,13 @@ def test_render():
MyNode("sub1", parent=root)
r = at.RenderTree(root)

expected = "\n".join(
[
"MyNode('|root')",
"├── MyNode('|root|sub0')",
"│ ├── MyNode('|root|sub0|sub0B')",
"│ └── MyNode('|root|sub0|sub0A')",
"└── MyNode('|root|sub1')",
]
)
if six.PY2:
eq_(str(r).decode("utf-8"), expected)
else:
eq_(str(r), expected)
assert str(r).splitlines() == [
"MyNode('|root')",
"├── MyNode('|root|sub0')",
"│ ├── MyNode('|root|sub0|sub0B')",
"│ └── MyNode('|root|sub0|sub0A')",
"└── MyNode('|root|sub1')",
]


def test_get():
Expand Down
126 changes: 71 additions & 55 deletions tests/test_render.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
# -*- coding: utf-8 -*-
import six
from helper import eq_, eq_str

import anytree


Expand All @@ -12,29 +9,22 @@ def test_render_str():
anytree.Node("sub0B", parent=s0)
anytree.Node("sub0A", parent=s0)
anytree.Node("sub1", parent=root)
r = anytree.RenderTree(root)

expected = "\n".join(
[
"Node('/root')",
"├── Node('/root/sub0')",
"│ ├── Node('/root/sub0/sub0B')",
"│ └── Node('/root/sub0/sub0A')",
"└── Node('/root/sub1')",
]
)
eq_str(str(r), expected)
r = anytree.RenderTree(root)
assert str(r).splitlines() == [
"Node('/root')",
"├── Node('/root/sub0')",
"│ ├── Node('/root/sub0/sub0B')",
"│ └── Node('/root/sub0/sub0A')",
"└── Node('/root/sub1')",
]

r = anytree.RenderTree(root, childiter=lambda nodes: [n for n in nodes if len(n.name) < 5])

expected = "\n".join(
[
"Node('/root')",
"├── Node('/root/sub0')",
"└── Node('/root/sub1')",
]
)
eq_str(str(r), expected)
assert str(r).splitlines() == [
"Node('/root')",
"├── Node('/root/sub0')",
"└── Node('/root/sub1')",
]


def test_render_repr():
Expand All @@ -43,11 +33,7 @@ def test_render_repr():
anytree.Node("sub", parent=root)
r = anytree.RenderTree(root)

if six.PY2:
expected = "RenderTree(Node('/root'), style=ContStyle(), " "childiter=<type 'list'>)"
else:
expected = "RenderTree(Node('/root'), style=ContStyle(), " "childiter=<class 'list'>)"
eq_(repr(r), expected)
assert repr(r) == "RenderTree(Node('/root'), style=ContStyle(), " "childiter=<class 'list'>)"


def test_render():
Expand All @@ -60,23 +46,21 @@ def test_render():

r = anytree.RenderTree(root, style=anytree.DoubleStyle)
result = [(pre, node) for pre, _, node in r]
expected = [
assert result == [
("", root),
("╠══ ", s0),
("║ ╠══ ", s0b),
("║ ╚══ ", s0a),
("╚══ ", s1),
]
eq_(result, expected)

def multi(root):
for pre, fill, node in anytree.RenderTree(root):
yield "%s%s" % (pre, node.lines[0]), node
for line in node.lines[1:]:
yield "%s%s" % (fill, line), node

result = list(multi(root))
expected = [
assert list(multi(root)) == [
("c0fe", root),
("c0de", root),
("├── ha", s0),
Expand All @@ -88,7 +72,6 @@ def multi(root):
("│ b", s0a),
("└── Z", s1),
]
eq_(result, expected)


def test_maxlevel():
Expand All @@ -100,42 +83,39 @@ def test_maxlevel():

r = anytree.RenderTree(root, maxlevel=2)
result = [(pre, node) for pre, _, node in r]
expected = [
assert result == [
("", root),
("├── ", s0),
("└── ", s1),
]
print(expected)
print(result)
eq_(result, expected)


def test_asciistyle():
style = anytree.AsciiStyle()
eq_(style.vertical, "| ")
eq_(style.cont, "|-- ")
eq_(style.end, "+-- ")
assert style.vertical == "| "
assert style.cont == "|-- "
assert style.end == "+-- "


def test_contstyle():
style = anytree.ContStyle()
eq_(style.vertical, "\u2502 ")
eq_(style.cont, "\u251c\u2500\u2500 ")
eq_(style.end, "\u2514\u2500\u2500 ")
assert style.vertical == "\u2502 "
assert style.cont == "\u251c\u2500\u2500 "
assert style.end == "\u2514\u2500\u2500 "


def test_controundstyle():
style = anytree.ContRoundStyle()
eq_(style.vertical, "\u2502 ")
eq_(style.cont, "\u251c\u2500\u2500 ")
eq_(style.end, "\u2570\u2500\u2500 ")
assert style.vertical == "\u2502 "
assert style.cont == "\u251c\u2500\u2500 "
assert style.end == "\u2570\u2500\u2500 "


def test_doublestyle():
style = anytree.DoubleStyle()
eq_(style.vertical, "\u2551 ")
eq_(style.cont, "\u2560\u2550\u2550 ")
eq_(style.end, "\u255a\u2550\u2550 ")
assert style.vertical == "\u2551 "
assert style.cont == "\u2560\u2550\u2550 "
assert style.end == "\u255a\u2550\u2550 "


def test_by_attr():
Expand All @@ -145,9 +125,45 @@ def test_by_attr():
anytree.Node("sub0B", parent=s0, lines=["sub", "0B"])
anytree.Node("sub0A", parent=s0)
anytree.Node("sub1", parent=root, lines=["sub1"])
eq_(anytree.RenderTree(root).by_attr(), "root\n├── sub0\n│ ├── sub0B\n│ └── sub0A\n└── sub1")
eq_(anytree.RenderTree(root).by_attr("lines"), "root\n├── su\n│ b0\n│ ├── sub\n│ │ 0B\n│ └── \n└── sub1")
eq_(
anytree.RenderTree(root).by_attr(lambda node: ":".join(node.name)),
"r:o:o:t\n├── s:u:b:0\n│ ├── s:u:b:0:B\n│ └── s:u:b:0:A\n└── s:u:b:1",
)

assert anytree.RenderTree(root).by_attr().splitlines() == [
"root",
"├── sub0",
"│ ├── sub0B",
"│ └── sub0A",
"└── sub1",
]
assert anytree.RenderTree(root).by_attr("lines").splitlines() == [
"root",
"├── su",
"│ b0",
"│ ├── sub",
"│ │ 0B",
"│ └── ",
"└── sub1",
]
assert anytree.RenderTree(root).by_attr(lambda node: ":".join(node.name)).splitlines() == [
"r:o:o:t",
"├── s:u:b:0",
"│ ├── s:u:b:0:B",
"│ └── s:u:b:0:A",
"└── s:u:b:1",
]


def test_repr():
"""Repr."""

class ReprNode(anytree.Node):
def __repr__(self):
return "{name}\n{name}".format(name=self.name)

root = ReprNode("root", lines=["root"])
s0 = ReprNode("sub0", parent=root, lines=["su", "b0"])
ReprNode("sub0B", parent=s0, lines=["sub", "0B"])
ReprNode("sub0A", parent=s0)
ReprNode("sub1", parent=root, lines=["sub1"])

bystr = str(anytree.RenderTree(root)).splitlines()
byident = anytree.RenderTree(root).by_attr(lambda node: node).splitlines()
assert bystr == byident

0 comments on commit e1e0265

Please sign in to comment.