Skip to content

Commit dd03f3f

Browse files
committed
v0.7.3 misc fix for print_tree, add unit tests, modify contributing
1 parent 80f1b9d commit dd03f3f

File tree

6 files changed

+162
-90
lines changed

6 files changed

+162
-90
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.7.3] - 2023-02-25
8+
### Added
9+
- Tree Export: Fixed `print_tree` checking attributes with `hasattr` to handle cases of null or 0 value attributes, add more test cases.
10+
- Contributing: Added more description.
711

812
## [0.7.2] - 2023-02-18
913
### Added
@@ -195,6 +199,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
195199
- Utility Iterator: Tree traversal methods.
196200
- Workflow To Do App: Tree use case with to-do list implementation.
197201

202+
[0.7.3]: https://github.com/kayjan/bigtree/compare/v0.7.2...v0.7.3
198203
[0.7.2]: https://github.com/kayjan/bigtree/compare/v0.7.1...v0.7.2
199204
[0.7.1]: https://github.com/kayjan/bigtree/compare/v0.7.0...v0.7.1
200205
[0.7.0]: https://github.com/kayjan/bigtree/compare/v0.6.10...v0.7.0

bigtree/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "0.7.2"
1+
__version__ = "0.7.3"
22

33
from bigtree.binarytree.construct import list_to_binarytree
44
from bigtree.dag.construct import dataframe_to_dag, dict_to_dag, list_to_dag

bigtree/tree/export.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ def print_tree(
3333
tree: Node,
3434
node_name_or_path: str = "",
3535
max_depth: int = None,
36-
attr_list: List[str] = None,
3736
all_attrs: bool = False,
38-
attr_omit_null: bool = True,
37+
attr_list: List[str] = None,
38+
attr_omit_null: bool = False,
3939
attr_bracket: List[str] = ["[", "]"],
4040
style: str = "const",
4141
custom_style: List[str] = [],
@@ -143,9 +143,9 @@ def print_tree(
143143
tree (Node): tree to print
144144
node_name_or_path (str): node to print from, becomes the root node of printing
145145
max_depth (int): maximum depth of tree to print, based on `depth` attribute, optional
146+
all_attrs (bool): indicator to show all attributes, overrides `attr_list` and `attr_omit_null`
146147
attr_list (list): list of node attributes to print, optional
147-
all_attrs (bool): indicator to show all attributes, overrides `attr_list`
148-
attr_omit_null (bool): indicator whether to omit showing of null attributes, defaults to True
148+
attr_omit_null (bool): indicator whether to omit showing of null attributes, defaults to False
149149
attr_bracket (List[str]): open and close bracket for `all_attrs` or `attr_list`
150150
style (str): style of print, defaults to abstract style
151151
custom_style (List[str]): style of stem, branch and final stem, used when `style` is set to 'custom'
@@ -179,6 +179,7 @@ def print_tree(
179179
attr_str_list = [
180180
f"{attr_name}={_node.get_attr(attr_name)}"
181181
for attr_name in attr_list
182+
if hasattr(_node, attr_name)
182183
]
183184
attr_str = ", ".join(attr_str_list)
184185
if attr_str:

docs/source/others/contributing.rst

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Developing
4040
-----------------------------------
4141

4242
After making your changes, create a new branch, add and commit your changed files.
43-
In this example, lets assume the changed file is `README.md`.
43+
In this example, lets assume the changed file is ``README.md``.
4444
If there are any pre-commit changes, do re-add and re-commit your files.
4545

4646
.. code-block:: bash
@@ -70,17 +70,11 @@ Make sure your add/update the tests and documentations accordingly.
7070
Convention and Standards
7171
-----------------------------------
7272

73-
When creating branches, it is recommended to create them in the format,
73+
When creating branches, it is recommended to create them in the format ``type/action``. For example,
7474

7575
.. code-block:: bash
7676
77-
type/action
78-
79-
For example,
80-
81-
.. code-block:: bash
82-
83-
git checkout -b feat/add-this
77+
$ git checkout -b feat/add-this
8478
8579
When performing commits, it is also recommended to follow `conventional commits <https://www.conventionalcommits.org/en/v1.0.0/>`_ when writing commit messages.
8680

tests/conftest.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,38 @@ def tree_node_no_attr():
182182
return a
183183

184184

185+
@pytest.fixture
186+
def tree_node_negative_null_attr():
187+
"""
188+
Tree should have structure
189+
a
190+
|-- b
191+
| |-- d
192+
| +-- e
193+
| |-- g
194+
| +-- h
195+
+-- c
196+
+-- f
197+
"""
198+
a = Node("a")
199+
b = Node("b", age=-1)
200+
c = Node("c", age=0)
201+
d = Node("d", age=1)
202+
e = Node("e", age=None)
203+
f = Node("f")
204+
g = Node("g")
205+
h = Node("h")
206+
207+
b.parent = a
208+
c.parent = a
209+
d.parent = b
210+
e.parent = b
211+
f.parent = c
212+
g.parent = e
213+
h.parent = e
214+
return a
215+
216+
185217
@pytest.fixture
186218
def tree_node_style():
187219
"""

tests/tree/test_export.py

Lines changed: 116 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -17,138 +17,178 @@
1717
)
1818
from tests.node.test_node import assert_tree_structure_node_root_generic
1919

20+
tree_node_str = """a [age=90]\n├── b [age=65]\n│ ├── d [age=40]\n│ └── e [age=35]\n│ ├── g [age=10]
21+
│ └── h [age=6]\n└── c [age=60]\n └── f [age=38]\n"""
22+
tree_node_no_attr_str = """a\n├── b\n│ ├── d\n│ └── e\n│ ├── g\n│ └── h\n└── c\n └── f\n"""
23+
2024

2125
class TestPrintTree:
2226
@staticmethod
23-
def test_print_tree_ansi(tree_node):
24-
expected_str = """a\n|-- b\n| |-- d\n| `-- e\n| |-- g\n| `-- h\n`-- c\n `-- f\n"""
25-
assert_print_statement(print_tree, expected_str, tree=tree_node, style="ansi")
27+
def test_print_tree_child_node_name(tree_node):
28+
expected_str = """b\n├── d\n└── e\n ├── g\n └── h\n"""
29+
assert_print_statement(
30+
print_tree,
31+
expected_str,
32+
tree=tree_node,
33+
node_name_or_path="b",
34+
)
2635

2736
@staticmethod
28-
def test_print_tree_ascii(tree_node):
29-
expected_str = """a\n|-- b\n| |-- d\n| +-- e\n| |-- g\n| +-- h\n+-- c\n +-- f\n"""
30-
assert_print_statement(print_tree, expected_str, tree=tree_node, style="ascii")
37+
def test_print_tree_child_node_path(tree_node):
38+
expected_str = """b\n├── d\n└── e\n ├── g\n └── h\n"""
39+
assert_print_statement(
40+
print_tree,
41+
expected_str,
42+
tree=tree_node,
43+
node_name_or_path="a/b",
44+
)
3145

46+
# all_attr
3247
@staticmethod
33-
def test_print_tree_const(tree_node):
34-
expected_str = """a\n├── b\n│ ├── d\n│ └── e\n│ ├── g\n│ └── h\n└── c\n └── f\n"""
35-
assert_print_statement(print_tree, expected_str, tree=tree_node, style="const")
48+
def test_print_tree_all_attr(tree_node):
49+
assert_print_statement(
50+
print_tree, tree_node_str, tree=tree_node, all_attrs=True
51+
)
3652

3753
@staticmethod
38-
def test_print_tree_const_bold(tree_node):
39-
expected_str = """a\n┣━━ b\n┃ ┣━━ d\n┃ ┗━━ e\n┃ ┣━━ g\n┃ ┗━━ h\n┗━━ c\n ┗━━ f\n"""
54+
def test_print_tree_all_attr_empty(tree_node_no_attr):
4055
assert_print_statement(
41-
print_tree, expected_str, tree=tree_node, style="const_bold"
56+
print_tree,
57+
tree_node_no_attr_str,
58+
tree=tree_node_no_attr,
59+
all_attrs=True,
4260
)
4361

62+
# attr_list
4463
@staticmethod
45-
def test_print_tree_rounded(tree_node):
46-
expected_str = """a\n├── b\n│ ├── d\n│ ╰── e\n│ ├── g\n│ ╰── h\n╰── c\n ╰── f\n"""
64+
def test_print_tree_attr_list(tree_node):
4765
assert_print_statement(
48-
print_tree, expected_str, tree=tree_node, style="rounded"
66+
print_tree, tree_node_str, tree=tree_node, attr_list=["age"]
4967
)
5068

5169
@staticmethod
52-
def test_print_tree_double(tree_node):
53-
expected_str = """a\n╠══ b\n║ ╠══ d\n║ ╚══ e\n║ ╠══ g\n║ ╚══ h\n╚══ c\n ╚══ f\n"""
54-
assert_print_statement(print_tree, expected_str, tree=tree_node, style="double")
70+
def test_print_tree_invalid_attr(tree_node):
71+
assert_print_statement(
72+
print_tree, tree_node_no_attr_str, tree=tree_node, attr_list=["random"]
73+
)
5574

75+
# attr_list, attr_omit_null
5676
@staticmethod
57-
def test_print_tree_custom(tree_node):
58-
expected_str = """a\nb\nd\ne\ng\nh\nc\nf\n"""
77+
def test_print_tree_attr_omit_null_false(tree_node_negative_null_attr):
78+
expected_str = """a\n├── b [age=-1]\n│ ├── d [age=1]\n│ └── e [age=None]\n│ ├── g\n│ └── h\n└── c [age=0]\n └── f\n"""
79+
assert_print_statement(
80+
print_tree,
81+
expected_str,
82+
tree=tree_node_negative_null_attr,
83+
attr_list=["age"],
84+
attr_omit_null=False,
85+
)
86+
87+
@staticmethod
88+
def test_print_tree_attr_omit_null_true(tree_node_negative_null_attr):
89+
expected_str = """a\n├── b [age=-1]\n│ ├── d [age=1]\n│ └── e\n│ ├── g\n│ └── h\n└── c\n └── f\n"""
5990
assert_print_statement(
6091
print_tree,
6192
expected_str,
93+
tree=tree_node_negative_null_attr,
94+
attr_list=["age"],
95+
attr_omit_null=True,
96+
)
97+
98+
# attr_bracket
99+
@staticmethod
100+
def test_print_tree_attr_bracket(tree_node):
101+
assert_print_statement(
102+
print_tree,
103+
tree_node_str.replace("[", "(").replace("]", ")"),
62104
tree=tree_node,
63-
style="custom",
64-
custom_style=["", "", ""],
105+
all_attrs=True,
106+
attr_bracket=["(", ")"],
65107
)
66108

67109
@staticmethod
68-
def test_print_tree_unknown_style(tree_node):
69-
with pytest.raises(ValueError):
70-
print_tree(tree_node, style="something")
110+
def test_print_tree_attr_bracket_missing_error(tree_node):
111+
with pytest.raises(ValueError) as exc_info:
112+
print_tree(tree_node, all_attrs=True, attr_bracket=[""])
113+
assert str(exc_info.value).startswith(
114+
"Expect open and close brackets in `attr_bracket`"
115+
)
71116

117+
# style
72118
@staticmethod
73-
def test_print_tree_no_attr(tree_node):
119+
def test_print_tree_style_ansi(tree_node):
74120
expected_str = """a\n|-- b\n| |-- d\n| `-- e\n| |-- g\n| `-- h\n`-- c\n `-- f\n"""
121+
assert_print_statement(print_tree, expected_str, tree=tree_node, style="ansi")
122+
123+
@staticmethod
124+
def test_print_tree_style_ascii(tree_node):
125+
expected_str = """a\n|-- b\n| |-- d\n| +-- e\n| |-- g\n| +-- h\n+-- c\n +-- f\n"""
126+
assert_print_statement(print_tree, expected_str, tree=tree_node, style="ascii")
127+
128+
@staticmethod
129+
def test_print_tree_style_const(tree_node):
130+
expected_str = """a\n├── b\n│ ├── d\n│ └── e\n│ ├── g\n│ └── h\n└── c\n └── f\n"""
131+
assert_print_statement(print_tree, expected_str, tree=tree_node, style="const")
132+
133+
@staticmethod
134+
def test_print_tree_style_const_bold(tree_node):
135+
expected_str = """a\n┣━━ b\n┃ ┣━━ d\n┃ ┗━━ e\n┃ ┣━━ g\n┃ ┗━━ h\n┗━━ c\n ┗━━ f\n"""
75136
assert_print_statement(
76-
print_tree, expected_str, tree=tree_node, attr_list=["random"], style="ansi"
137+
print_tree, expected_str, tree=tree_node, style="const_bold"
77138
)
78139

79140
@staticmethod
80-
def test_print_tree_child_node_name(tree_node):
81-
expected_str = """b\n|-- d\n`-- e\n |-- g\n `-- h\n"""
141+
def test_print_tree_style_rounded(tree_node):
142+
expected_str = """a\n├── b\n│ ├── d\n│ ╰── e\n ├── g\n ╰── h\n╰── c\n ╰── f\n"""
82143
assert_print_statement(
83-
print_tree,
84-
expected_str,
85-
tree=tree_node,
86-
node_name_or_path="b",
87-
style="ansi",
144+
print_tree, expected_str, tree=tree_node, style="rounded"
88145
)
89146

90147
@staticmethod
91-
def test_print_tree_child_node_path(tree_node):
92-
expected_str = """b\n|-- d\n`-- e\n |-- g\n `-- h\n"""
148+
def test_print_tree_style_double(tree_node):
149+
expected_str = """a\n╠══ b\n║ ╠══ d\n║ ╚══ e\n║ ╠══ g\n║ ╚══ h\n╚══ c\n ╚══ f\n"""
150+
assert_print_statement(print_tree, expected_str, tree=tree_node, style="double")
151+
152+
@staticmethod
153+
def test_print_tree_style_unknown_error(tree_node):
154+
with pytest.raises(ValueError) as exc_info:
155+
print_tree(tree_node, style="something")
156+
assert str(exc_info.value).startswith("Choose one of")
157+
158+
# custom_style
159+
@staticmethod
160+
def test_print_tree_custom_style(tree_node):
161+
expected_str = """a\nb\nd\ne\ng\nh\nc\nf\n"""
93162
assert_print_statement(
94163
print_tree,
95164
expected_str,
96165
tree=tree_node,
97-
node_name_or_path="a/b",
98-
style="ansi",
166+
style="custom",
167+
custom_style=["", "", ""],
99168
)
100169

101170
@staticmethod
102-
def test_print_tree_unequal_char(tree_node):
103-
with pytest.raises(ValueError):
171+
def test_print_tree_custom_style_unequal_char_error(tree_node):
172+
with pytest.raises(ValueError) as exc_info:
104173
print_tree(
105174
tree_node,
106175
style="custom",
107176
custom_style=["", " ", ""],
108177
)
178+
assert str(exc_info.value).startswith(
179+
"`style_stem`, `style_branch`, and `style_stem_final` are of different length"
180+
)
109181

110182
@staticmethod
111-
def test_print_tree_missing_style(tree_node):
112-
with pytest.raises(ValueError):
183+
def test_print_tree_custom_style_missing_style_error(tree_node):
184+
with pytest.raises(ValueError) as exc_info:
113185
print_tree(
114186
tree_node,
115187
style="custom",
116188
custom_style=["", ""],
117189
)
118-
119-
@staticmethod
120-
def test_print_tree_missing_bracket(tree_node):
121-
with pytest.raises(ValueError):
122-
print_tree(tree_node, all_attrs=True, attr_bracket=[""])
123-
124-
@staticmethod
125-
def test_print_tree_attr(tree_node):
126-
expected_str = """a [age=90]\n|-- b [age=65]\n| |-- d [age=40]\n| `-- e [age=35]\n| |-- g [age=10]\n| `-- h [age=6]\n`-- c [age=60]\n `-- f [age=38]\n"""
127-
assert_print_statement(
128-
print_tree,
129-
expected_str,
130-
tree=tree_node,
131-
attr_list=["age"],
132-
attr_omit_null=False,
133-
style="ansi",
134-
)
135-
136-
@staticmethod
137-
def test_print_tree_all_attr(tree_node):
138-
expected_str = """a [age=90]\n|-- b [age=65]\n| |-- d [age=40]\n| `-- e [age=35]\n| |-- g [age=10]\n| `-- h [age=6]\n`-- c [age=60]\n `-- f [age=38]\n"""
139-
assert_print_statement(
140-
print_tree, expected_str, tree=tree_node, all_attrs=True, style="ansi"
141-
)
142-
143-
@staticmethod
144-
def test_print_tree_all_attr_empty(tree_node_no_attr):
145-
expected_str = """a\n|-- b\n| |-- d\n| `-- e\n| |-- g\n| `-- h\n`-- c\n `-- f\n"""
146-
assert_print_statement(
147-
print_tree,
148-
expected_str,
149-
tree=tree_node_no_attr,
150-
all_attrs=True,
151-
style="ansi",
190+
assert str(exc_info.value).startswith(
191+
"Custom style selected, please specify the style of stem, branch, and final stem in `custom_style`"
152192
)
153193

154194

0 commit comments

Comments
 (0)