A Python implementation of tree data structure with hierarchical organization and efficient operations for traversal, modification, search, and visualization.
Comprehensive functionality for dynamically modifying tree structures including moving nodes, removing branches, copying subtrees, and updating node properties while maintaining data integrity and relationships.
Move nodes within the tree to reorganize structure while preserving subtree relationships.
def move_node(source, destination) -> None:
"""
Move a node and its subtree to a new parent.
Parameters:
- source: str, identifier of node to move
- destination: str, identifier of new parent node
Raises:
LoopError if move would create circular reference
NodeIDAbsentError if source or destination doesn't exist
"""Usage Examples:
# Move employee to different department
tree.move_node("alice", "sales_dept")
# Reorganize departments
tree.move_node("engineering", "technology_division")
# Move multiple nodes
employees_to_transfer = ["bob", "carol", "dave"]
for emp_id in employees_to_transfer:
tree.move_node(emp_id, "new_department")
# Move with error handling
try:
tree.move_node("manager", "subordinate") # Would create loop
except LoopError:
print("Cannot move manager under subordinate")Remove individual nodes or entire subtrees from the tree structure.
def remove_node(identifier) -> int:
"""
Remove a node and all its descendants from the tree.
Parameters:
- identifier: str, node identifier to remove
Returns:
int, number of nodes removed (including descendants)
Raises:
NodeIDAbsentError if node doesn't exist
"""
def link_past_node(nid) -> None:
"""
Remove node while connecting its children to its parent.
Parameters:
- nid: str, node identifier to remove
Raises:
LinkPastRootNodeError if attempting to link past root
NodeIDAbsentError if node doesn't exist
"""Usage Examples:
# Remove employee and report structure
removed_count = tree.remove_node("manager_leaving")
print(f"Removed {removed_count} nodes from organization")
# Remove department but keep employees
tree.link_past_node("temp_department") # Employees move up to parent
# Conditional removal
nodes_to_remove = []
for node_id in tree.expand_tree():
node = tree[node_id]
if node.data and node.data.get("status") == "inactive":
nodes_to_remove.append(node_id)
for node_id in nodes_to_remove:
tree.remove_node(node_id)
# Safe removal with validation
if "old_project" in tree:
if not tree.children("old_project"): # Check if has children
tree.remove_node("old_project")
else:
print("Cannot remove project with active sub-projects")Update node attributes and data while maintaining tree structure.
def update_node(nid, **attrs) -> None:
"""
Update node attributes.
Parameters:
- nid: str, node identifier
- **attrs: keyword arguments for node attributes (tag, data, etc.)
Raises:
NodeIDAbsentError if node doesn't exist
"""Usage Examples:
# Update node tag (display name)
tree.update_node("emp_001", tag="Alice Smith (Senior Engineer)")
# Update node data
tree.update_node("emp_001", data={
"name": "Alice Smith",
"role": "Senior Engineer",
"salary": 125000,
"department": "Engineering"
})
# Multiple attribute update
tree.update_node("project_x",
tag="Project X - Phase 2",
data={"status": "active", "budget": 500000})
# Conditional updates
for node_id in tree.expand_tree():
node = tree[node_id]
if node.data and node.data.get("role") == "intern":
tree.update_node(node_id,
tag=f"{node.tag} (Full-time)",
data={**node.data, "role": "junior"})Create, extract, and manipulate subtrees as independent tree structures.
def subtree(nid, identifier=None) -> Tree:
"""
Create a shallow copy of subtree rooted at specified node.
Parameters:
- nid: str, root node identifier for subtree
- identifier: str, identifier for new tree (optional)
Returns:
Tree, new tree containing the subtree
Raises:
NodeIDAbsentError if node doesn't exist
"""
def remove_subtree(nid, identifier=None) -> Tree:
"""
Extract subtree from tree (removes from original).
Parameters:
- nid: str, root node identifier for subtree
- identifier: str, identifier for extracted tree (optional)
Returns:
Tree, extracted subtree as independent tree
"""Usage Examples:
# Create copy of department subtree
eng_dept_copy = tree.subtree("engineering")
eng_dept_copy.show()
# Extract department for restructuring
sales_dept = tree.remove_subtree("sales")
print(f"Extracted {sales_dept.size()} person sales department")
# Process subtrees independently
all_departments = ["engineering", "sales", "hr"]
dept_trees = {}
for dept_id in all_departments:
if dept_id in tree:
dept_trees[dept_id] = tree.subtree(dept_id)
# Analyze subtrees
for dept_name, dept_tree in dept_trees.items():
employee_count = dept_tree.size() - 1 # Exclude department head
print(f"{dept_name}: {employee_count} employees")Combine trees and paste subtrees to build complex structures.
def paste(nid, new_tree, deep=False) -> None:
"""
Paste another tree as subtree under specified node.
Parameters:
- nid: str, parent node identifier where tree will be pasted
- new_tree: Tree, tree to paste as subtree
- deep: bool, perform deep copy if True
Raises:
NodeIDAbsentError if parent node doesn't exist
"""
def merge(nid, new_tree, deep=False) -> None:
"""
Merge another tree's children under specified node.
Parameters:
- nid: str, parent node identifier
- new_tree: Tree, tree whose children will be merged
- deep: bool, perform deep copy if True
"""Usage Examples:
# Create new subsidiary structure
subsidiary = Tree()
subsidiary.create_node("Subsidiary Corp", "sub_corp")
subsidiary.create_node("Sub Engineering", "sub_eng", parent="sub_corp")
subsidiary.create_node("Sub Sales", "sub_sales", parent="sub_corp")
# Paste entire subsidiary under main company
tree.paste("company", subsidiary)
# Merge departments from acquired company
acquired_company = Tree()
acquired_company.create_node("Acquired Corp", "acq_corp")
acquired_company.create_node("R&D Department", "rd", parent="acq_corp")
acquired_company.create_node("Marketing", "marketing", parent="acq_corp")
# Merge just the departments (not the company node)
tree.merge("company", acquired_company)
# Deep copy for independent data
secure_backup = tree.subtree("sensitive_project")
tree.paste("backup_location", secure_backup, deep=True)Complex modification scenarios and bulk operations.
Usage Examples:
# Bulk reorganization
def reorganize_by_role():
# Group employees by role
roles = {}
for node_id in tree.expand_tree():
node = tree[node_id]
if node.data and "role" in node.data:
role = node.data["role"]
if role not in roles:
roles[role] = []
roles[role].append(node_id)
# Create role-based departments
for role, employee_ids in roles.items():
dept_id = f"{role}_dept"
tree.create_node(f"{role.title()} Department", dept_id, parent="company")
for emp_id in employee_ids:
tree.move_node(emp_id, dept_id)
# Conditional restructuring
def flatten_single_child_branches():
"""Remove nodes that have only one child by linking past them."""
nodes_to_remove = []
for node_id in tree.expand_tree():
children = tree.children(node_id)
if len(children) == 1 and not tree.is_root(node_id):
nodes_to_remove.append(node_id)
for node_id in nodes_to_remove:
tree.link_past_node(node_id)
# Tree surgery - replace subtree
def replace_subtree(old_root_id, new_subtree):
"""Replace an existing subtree with a new one."""
parent_id = tree.parent(old_root_id).identifier if tree.parent(old_root_id) else None
tree.remove_node(old_root_id)
if parent_id:
tree.paste(parent_id, new_subtree)
else:
# Replacing root - more complex operation
tree.nodes.clear()
tree.paste(None, new_subtree)
# Batch updates with transaction-like behavior
def batch_update(updates):
"""Apply multiple updates with rollback on error."""
backup = Tree(tree, deep=True) # Create backup
try:
for update_type, *args in updates:
if update_type == "move":
tree.move_node(*args)
elif update_type == "remove":
tree.remove_node(*args)
elif update_type == "update":
tree.update_node(*args)
except Exception as e:
# Rollback on error
tree.nodes = backup.nodes
tree.root = backup.root
raise e
# Usage of batch update
updates = [
("move", "alice", "new_dept"),
("update", "bob", {"role": "senior"}),
("remove", "old_project")
]
batch_update(updates)Validate tree integrity after modifications.
Usage Examples:
def validate_tree_integrity():
"""Validate tree structure after modifications."""
issues = []
# Check for orphaned nodes
for node_id, node in tree.nodes.items():
if node_id != tree.root:
parent_id = tree.parent(node_id)
if not parent_id or parent_id.identifier not in tree.nodes:
issues.append(f"Orphaned node: {node_id}")
# Check for circular references
visited = set()
def check_cycles(node_id, path):
if node_id in path:
issues.append(f"Circular reference: {path + [node_id]}")
return
if node_id in visited:
return
visited.add(node_id)
for child in tree.children(node_id):
check_cycles(child.identifier, path + [node_id])
if tree.root:
check_cycles(tree.root, [])
return issues
# Validate after major changes
issues = validate_tree_integrity()
if issues:
print("Tree integrity issues found:")
for issue in issues:
print(f" - {issue}")class LoopError(Exception):
"""Raised when operation would create circular reference"""
class LinkPastRootNodeError(Exception):
"""Raised when attempting to link past root node"""
class NodeIDAbsentError(Exception):
"""Raised when referencing non-existent node"""Usage Examples:
from treelib import LoopError, LinkPastRootNodeError, NodeIDAbsentError
# Safe node movement
def safe_move_node(source, destination):
try:
tree.move_node(source, destination)
return True
except LoopError:
print(f"Cannot move {source} to {destination}: would create loop")
return False
except NodeIDAbsentError as e:
print(f"Node not found: {e}")
return False
# Safe link past operation
def safe_link_past(node_id):
try:
tree.link_past_node(node_id)
return True
except LinkPastRootNodeError:
print("Cannot link past root node")
return False
except NodeIDAbsentError:
print(f"Node {node_id} not found")
return FalseInstall with Tessl CLI
npx tessl i tessl/pypi-treelib