Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
814b96c
test: rename variables
kayjan Dec 22, 2025
36ab948
test: rename variables + ensure tree helper methods return Tree type
kayjan Dec 22, 2025
2ad9efc
docs: enhance docstring
kayjan Dec 22, 2025
de611a0
feat: add get_subtree as plugin
kayjan Dec 22, 2025
5f688cf
docs: enhance docstring
kayjan Dec 22, 2025
3fb803f
docs: update CHANGELOG
kayjan Dec 22, 2025
92870a6
test: rename variables
kayjan Dec 22, 2025
1c9a47f
Merge branch 'master' of https://github.com/kayjan/bigtree
kayjan Dec 22, 2025
c68f415
Merge branch 'master' of https://github.com/kayjan/bigtree
kayjan Jan 18, 2026
8b2cc86
Merge branch 'master' of https://github.com/kayjan/bigtree
kayjan Jan 18, 2026
d8e1a90
Merge branch 'master' of https://github.com/kayjan/bigtree
kayjan Jan 18, 2026
938c427
Merge branch 'master' of https://github.com/kayjan/bigtree
kayjan Jan 18, 2026
25f5abd
feat: get_attr to support nested attr
kayjan Jan 19, 2026
d6f2e86
bump: v1.3.0
kayjan Jan 22, 2026
36055f3
test: handle warning from polar tests
kayjan Jan 24, 2026
2180ed8
test: handle warning from polar tests
kayjan Jan 25, 2026
5960dcb
fix: handle pandas v3
kayjan Jan 25, 2026
1074e4b
fix: handle pandas v3
kayjan Jan 25, 2026
a3c34a1
fix: handle pandas v3
kayjan Jan 25, 2026
1d57ce2
fix: handle pandas v3
kayjan Jan 25, 2026
feed60e
docs: more examples for listing directories
kayjan Jan 29, 2026
79d3274
Merge branch 'master' of https://github.com/kayjan/bigtree into misc/fix
kayjan Jan 29, 2026
4e280f9
docs: more examples for listing directories
kayjan Jan 29, 2026
a78fb6f
docs: more examples for listing directories
kayjan Jan 29, 2026
9708942
docs: Fix CHANGELOG
kayjan Jan 29, 2026
5190bdf
bump: update CHANGELOG
kayjan Feb 13, 2026
a23cdf8
fix: Fix attr square brackets being confused as rich formatting
kayjan Feb 13, 2026
f35c737
Merge branch 'master' of https://github.com/kayjan/bigtree into feat/…
kayjan Feb 13, 2026
c790bcc
bump: v1.3.1
kayjan Feb 13, 2026
23d5d3c
feat: construct tree using rich using Tree.from_rich
kayjan Feb 13, 2026
93bd617
feat: construct tree using rich using Tree.from_rich
kayjan Feb 13, 2026
d0c902c
docs: Update docs
kayjan Feb 13, 2026
6bcdc9f
feat: add exceptions
kayjan Feb 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added:
- Docs: More examples for listing directories.
### Fixed:
- Docs: Some grammar improvements in docstring.
- Setup: Test with different Python environments.

## [1.3.1] - 2026-02-13
### Added:
- Tree Construct: Construct tree from rich tree using `rich_to_tree` or `Tree.from_rich`.
- Docs: More examples for listing directories.
### Fixed:
- Tree Export: Fix attr square brackets being confused as rich formatting.
Expand Down Expand Up @@ -905,7 +901,8 @@ ignore null attribute columns.
- Utility Iterator: Tree traversal methods.
- Workflow To Do App: Tree use case with to-do list implementation.

[Unreleased]: https://github.com/kayjan/bigtree/compare/1.3.0...HEAD
[Unreleased]: https://github.com/kayjan/bigtree/compare/1.3.1...HEAD
[1.3.1]: https://github.com/kayjan/bigtree/compare/1.3.0...1.3.1
[1.3.0]: https://github.com/kayjan/bigtree/compare/1.2.0...1.3.0
[1.2.0]: https://github.com/kayjan/bigtree/compare/1.1.0...1.2.0
[1.1.0]: https://github.com/kayjan/bigtree/compare/1.0.4...1.1.0
Expand Down
1 change: 1 addition & 0 deletions bigtree/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
polars_to_tree,
polars_to_tree_by_relation,
render_tree,
rich_to_tree,
str_to_tree,
)
from bigtree.tree.export import (
Expand Down
1 change: 1 addition & 0 deletions bigtree/_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def register_tree_plugins() -> None:
"from_list_relation": construct.list_to_tree_by_relation,
"from_str": construct.str_to_tree,
"from_newick": construct.newick_to_tree,
"from_rich": construct.rich_to_tree,
},
method="class",
)
Expand Down
3 changes: 2 additions & 1 deletion bigtree/tree/construct/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
)
from .lists import list_to_tree, list_to_tree_by_relation
from .render import render_tree
from .strings import add_path_to_tree, newick_to_tree, str_to_tree
from .strings import add_path_to_tree, newick_to_tree, rich_to_tree, str_to_tree

__all__ = [
"add_dataframe_to_tree_by_name",
Expand All @@ -38,5 +38,6 @@
"render_tree",
"add_path_to_tree",
"newick_to_tree",
"rich_to_tree",
"str_to_tree",
]
66 changes: 66 additions & 0 deletions bigtree/tree/construct/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,19 @@
from bigtree.tree import search
from bigtree.utils import assertions, constants, exceptions

try:
import rich
except ImportError: # pragma: no cover
from unittest.mock import MagicMock

rich = MagicMock()


__all__ = [
"add_path_to_tree",
"str_to_tree",
"newick_to_tree",
"rich_to_tree",
]

T = TypeVar("T", bound=node.Node)
Expand Down Expand Up @@ -469,3 +478,60 @@ def _raise_value_error(tree_idx: int) -> None:
current_depth,
)
return current_node


def rich_to_tree(
rich_tree: rich.tree.Tree, node_format_attr: str = "style"
) -> node.Node:
"""Construct tree from rich tree to allow more tree operations, return root of tree.

If the rich node label is of type rich.text.Text, it will save the style, if any, as
a node attribute. This does not support inferring rich node label string using
square brackets.

Examples:
>>> from bigtree import Tree
>>> from rich.tree import Tree as RichTree
>>> from rich.text import Text
>>> rich_root = RichTree(Text("Grandparent", style="magenta"))
>>> child = rich_root.add("Child")
>>> _ = child.add(Text("Grandchild", style="red"))
>>> _ = rich_root.add("Child 2")
>>> tree = Tree.rich_to_tree(rich_root)
>>> tree.show(all_attrs=True) # Try with rich=True, node_format_attr="style"
Grandparent [style=magenta]
├── Child
│ └── Grandchild [style=red]
└── Child 2

Args:
rich_tree: rich Tree
node_format_attr: attribute name for the node format

Returns:
Node
"""

def extract_label_and_style(label: rich.text.Text | str) -> tuple[str, str | None]:
"""Extract label and list of styles spans from a rich label.

Returns:
label and style (if label is rich.text.Text and has style)
"""
if isinstance(label, rich.text.Text):
return label.plain, label.style
else:
return str(label), None

def convert_node(
rich_node: rich.tree.Tree, parent: node.Node | None = None
) -> node.Node:
node_name, node_format = extract_label_and_style(rich_node.label)
_node = node.Node(node_name, parent=parent)
if node_format:
_node.set_attrs({node_format_attr: node_format})
for child in rich_node.children:
convert_node(child, parent=_node)
return _node

return convert_node(rich_tree)
7 changes: 7 additions & 0 deletions bigtree/tree/export/stdout.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,13 @@ def print_tree(
# Handle specific case where [ will be mistaken as rich formatting
attr_bracket = ("\\" + attr_bracket[0], attr_bracket[1])

# Handle specific case where [ will be mistaken as rich formatting
attr_bracket = (
("\\" + attr_bracket[0], attr_bracket[1])
if attr_bracket[0] == "["
else attr_bracket
)

for pre_str, fill_str, _node in yield_tree(
tree=tree,
node_name_or_path=node_name_or_path,
Expand Down
1 change: 0 additions & 1 deletion tests/tree/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,6 @@ def rich_console():
from rich.console import Console

return Console(
# no_color=True,
record=False,
color_system=None,
force_terminal=False,
Expand Down
14 changes: 14 additions & 0 deletions tests/tree/construct/conftest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import pytest

from bigtree.node import node


Expand All @@ -10,3 +12,15 @@ def __init__(self, name: str, custom_field: int, custom_field_str: str, **kwargs
super().__init__(name, **kwargs)
self.custom_field = custom_field
self.custom_field_str = custom_field_str


@pytest.fixture
def rich_root():
from rich.text import Text
from rich.tree import Tree as RichTree

rich_root = RichTree(Text("Grandparent", style="magenta"))
child = rich_root.add("Child")
_ = child.add(Text("Grandchild", style="red"))
_ = rich_root.add("Child 2")
return rich_root
53 changes: 53 additions & 0 deletions tests/tree/construct/test_strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,3 +531,56 @@ def assert_tree_structure_phylogenetic_attr(root, attrs=["B", "D", "E", "S", "le
assert (
expected == actual
), f"Expected length to be {expected}, received {actual}"


class TestRichtoTree:
@staticmethod
def test_rich_to_tree(rich_root, rich_console):
from bigtree.tree import export
from tests.conftest import assert_print_statement

root = construct.rich_to_tree(rich_root)
root_str = "Grandparent\n" "├── Child\n" "│ └── Grandchild\n" "└── Child 2\n"
assert_print_statement(
export.print_tree,
root_str,
tree=root,
rich=True,
console=rich_console,
)

@staticmethod
def test_rich_to_tree_attr(rich_root, rich_console):
from bigtree.tree import export
from tests.conftest import assert_print_statement

root = construct.rich_to_tree(rich_root)
root_str_attr = (
"Grandparent [style=magenta]\n"
"├── Child\n"
"│ └── Grandchild [style=red]\n"
"└── Child 2\n"
)
assert_print_statement(
export.print_tree,
root_str_attr,
tree=root,
all_attrs=True,
)
assert_print_statement(
export.print_tree,
root_str_attr,
tree=root,
all_attrs=True,
rich=True,
console=rich_console,
)
assert_print_statement(
export.print_tree,
root_str_attr,
tree=root,
all_attrs=True,
rich=True,
node_format_attr="style",
console=rich_console,
)
17 changes: 17 additions & 0 deletions tests/tree/test_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,23 @@ def test_from_newick():
assert_tree_structure_basenode_tree(tree)
assert_tree_structure_basenode_root(tree.node)

@staticmethod
def test_from_rich():
from rich.text import Text
from rich.tree import Tree as RichTree

rich_root = RichTree(Text("a", style="magenta"))
b = rich_root.add("b", style="red")
_ = b.add("d")
e = b.add("e")
_ = e.add("g", style="yellow")
_ = e.add("h", style="yellow")
c = rich_root.add("c", style="red")
_ = c.add("f")
tree = Tree.from_rich(rich_root)
assert_tree_structure_basenode_tree(tree)
assert_tree_structure_basenode_root(tree.node)


class TestTreeAdd(unittest.TestCase):

Expand Down