Skip to content

Commit cf32279

Browse files
authored
Merge pull request #308 from kayjan/bugfix/tree-diff
Fix get_tree_diff
2 parents c1c55a9 + 0dcc7f2 commit cf32279

File tree

6 files changed

+91
-13
lines changed

6 files changed

+91
-13
lines changed

CHANGELOG.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
## [0.21.3] - 2024-10-16
10+
### Added:
11+
- Tree Node: Docstring indentation and additional information for Node creation.
12+
- Misc: GitHub star diagram to README file.
13+
### Changed:
14+
- Tree Helper: Get tree diff to handle `sep` that are different for tree and other_tree, this will throw error now.
15+
- Tree Helper: Get tree diff to handle `sep` that contains forbidden symbols, this will change the `sep` to a fallback sep.
16+
- Tree Helper: Get tree diff to sort tree before returning the tree diff.
17+
### Fixed:
18+
- [#306] Tree Helper: Get tree diff to handle `sep` that is different from default.
19+
920
## [0.21.2] - 2024-10-14
1021
### Added:
1122
- Misc: Pull request template.
@@ -657,7 +668,8 @@ ignore null attribute columns.
657668
- Utility Iterator: Tree traversal methods.
658669
- Workflow To Do App: Tree use case with to-do list implementation.
659670

660-
[Unreleased]: https://github.com/kayjan/bigtree/compare/0.21.2...HEAD
671+
[Unreleased]: https://github.com/kayjan/bigtree/compare/0.21.3...HEAD
672+
[0.21.3]: https://github.com/kayjan/bigtree/compare/0.21.2...0.21.3
661673
[0.21.2]: https://github.com/kayjan/bigtree/compare/0.21.1...0.21.2
662674
[0.21.1]: https://github.com/kayjan/bigtree/compare/0.21.0...0.21.1
663675
[0.21.0]: https://github.com/kayjan/bigtree/compare/0.20.1...0.21.0

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,9 @@ To install `bigtree` with conda, run the following line in command prompt:
151151
```console
152152
$ conda install -c conda-forge bigtree
153153
```
154+
155+
-----
156+
157+
## Star History
158+
159+
[![Star History Chart](https://api.star-history.com/svg?repos=kayjan/bigtree&type=Date)](https://star-history.com/#kayjan/bigtree&Date)

bigtree/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "0.21.2"
1+
__version__ = "0.21.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/helper.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ def get_tree_diff(
242242
other_tree: node.Node,
243243
only_diff: bool = True,
244244
attr_list: List[str] = [],
245+
fallback_sep: str = "/",
245246
) -> node.Node:
246247
"""Get difference of `tree` to `other_tree`, changes are relative to `tree`.
247248
@@ -254,6 +255,12 @@ def get_tree_diff(
254255
- For example: (+) refers to nodes that are in `other_tree` but not `tree`.
255256
- For example: (-) refers to nodes that are in `tree` but not `other_tree`.
256257
258+
!!! note
259+
260+
- tree and other_tree must have the same `sep` symbol, otherwise this will raise ValueError
261+
- If the `sep` symbol contains one of `+` / `-` / `~` character, a fallback sep will be used
262+
- Node names in tree and other_tree must not contain the `sep` (or fallback sep) symbol
263+
257264
Examples:
258265
>>> # Create original tree
259266
>>> from bigtree import Node, get_tree_diff, list_to_tree
@@ -333,11 +340,25 @@ def get_tree_diff(
333340
other_tree (Node): tree to be compared with
334341
only_diff (bool): indicator to show all nodes or only nodes that are different (+/-), defaults to True
335342
attr_list (List[str]): tree attributes to check for difference, defaults to empty list
343+
fallback_sep (str): sep to fall back to if tree and other_tree has sep that clashes with symbols "+" / "-" / "~".
344+
All node names in tree and other_tree should not contain this fallback_sep, defaults to "/"
336345
337346
Returns:
338347
(Node)
339348
"""
340-
other_tree.sep = tree.sep
349+
if tree.sep != other_tree.sep:
350+
raise ValueError("`sep` must be the same for tree and other_tree")
351+
352+
forbidden_sep_symbols = ["+", "-", "~"]
353+
if any(
354+
forbidden_sep_symbol in tree.sep
355+
for forbidden_sep_symbol in forbidden_sep_symbols
356+
):
357+
tree = tree.copy()
358+
other_tree = other_tree.copy()
359+
tree.sep = fallback_sep
360+
other_tree.sep = fallback_sep
361+
341362
name_col = "name"
342363
path_col = "PATH"
343364
indicator_col = "Exists"
@@ -403,9 +424,11 @@ def get_tree_diff(
403424
(data_both[indicator_col] != "both")
404425
| (data_both[path_col].isin(path_changes_deque))
405426
]
406-
data_both = data_both[[path_col]]
427+
data_both = data_both[[path_col]].sort_values(path_col)
407428
if len(data_both):
408-
tree_diff = construct.dataframe_to_tree(data_both, node_type=tree.__class__)
429+
tree_diff = construct.dataframe_to_tree(
430+
data_both, node_type=tree.__class__, sep=tree.sep
431+
)
409432
# Handle tree attribute difference
410433
if len(path_changes_deque):
411434
path_changes_list = sorted(path_changes_deque, reverse=True)

tests/test_constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ class Constants:
169169
ERROR_NODE_PRUNE_NOT_FOUND = (
170170
"Cannot find any node matching path_name ending with {prune_path}"
171171
)
172+
ERROR_NODE_TREE_DIFF_DIFF_SEP = "`sep` must be the same for tree and other_tree"
172173

173174
# tree/modify
174175
ERROR_MODIFY_PARAM_TYPE = (

tests/tree/test_helper.py

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -236,22 +236,58 @@ def test_tree_diff(tree_node):
236236
assert_print_statement(export.print_tree, expected_str, tree=tree_only_diff)
237237

238238
@staticmethod
239-
def test_tree_diff_diff_sep(tree_node):
239+
def test_tree_diff_diff_sep_error(tree_node):
240240
other_tree_node = helper.prune_tree(tree_node, "a/c")
241-
_ = node.Node("d", parent=other_tree_node)
242241
other_tree_node.sep = "-"
242+
with pytest.raises(ValueError) as exc_info:
243+
helper.get_tree_diff(tree_node, other_tree_node)
244+
assert str(exc_info.value) == Constants.ERROR_NODE_TREE_DIFF_DIFF_SEP
245+
246+
@staticmethod
247+
def test_tree_diff_sep_clash_with_node_name_error(tree_node):
248+
other_tree_node = helper.prune_tree(tree_node, "a/c")
249+
_ = node.Node("/d", parent=other_tree_node)
250+
with pytest.raises(exceptions.TreeError) as exc_info:
251+
helper.get_tree_diff(tree_node, other_tree_node)
252+
assert str(exc_info.value) == Constants.ERROR_NODE_NAME
253+
254+
@staticmethod
255+
def test_tree_diff_sep_clash_with_node_name(tree_node):
256+
other_tree_node = helper.prune_tree(tree_node, "a/c")
257+
_ = node.Node("/d", parent=other_tree_node)
258+
tree_node.sep = "."
259+
other_tree_node.sep = "."
243260
tree_only_diff = helper.get_tree_diff(tree_node, other_tree_node)
244261
expected_str = (
245262
"a\n"
246-
"├── b (-)\n"
247-
"│ ├── d (-)\n"
248-
" ── e (-)\n"
249-
" ── g (-)\n"
250-
" ── h (-)\n"
251-
"└── d (+)\n"
263+
"├── /d (+)\n"
264+
"── b (-)\n"
265+
" ── d (-)\n"
266+
" ── e (-)\n"
267+
" ── g (-)\n"
268+
" └── h (-)\n"
252269
)
253270
assert_print_statement(export.print_tree, expected_str, tree=tree_only_diff)
254271

272+
@staticmethod
273+
def test_tree_diff_forbidden_sep(tree_node):
274+
other_tree_node = helper.prune_tree(tree_node, "a/c")
275+
_ = node.Node("d", parent=other_tree_node)
276+
for symbol in [".", "-", "+", "~"]:
277+
tree_node.sep = symbol
278+
other_tree_node.sep = symbol
279+
tree_only_diff = helper.get_tree_diff(tree_node, other_tree_node)
280+
expected_str = (
281+
"a\n"
282+
"├── b (-)\n"
283+
"│ ├── d (-)\n"
284+
"│ └── e (-)\n"
285+
"│ ├── g (-)\n"
286+
"│ └── h (-)\n"
287+
"└── d (+)\n"
288+
)
289+
assert_print_statement(export.print_tree, expected_str, tree=tree_only_diff)
290+
255291
@staticmethod
256292
def test_tree_diff_all_diff(tree_node):
257293
other_tree_node = helper.prune_tree(tree_node, "a/c")

0 commit comments

Comments
 (0)