Powerful and Lightweight Python Tree Data Structure with various plugins
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Export tree structures to various formats (dict, JSON, DOT for Graphviz, Mermaid) and import from structured data formats for data exchange, visualization, and interoperability with other systems.
Export tree structures to Python dictionary format for serialization, data processing, or conversion to other formats.
class DictExporter:
"""
Export tree to dictionary format.
Args:
dictcls: Dictionary class to use (default dict)
attriter: Function to filter/transform node attributes
childiter: Function to determine child iteration order (default list)
maxlevel: Maximum depth to export (default None - export all)
"""
def __init__(self, dictcls=dict, attriter=None, childiter=list, maxlevel=None): ...
def export(self, node): ...Usage Example:
from anytree import Node, DictExporter
# Create tree structure
root = Node("Company", founded=2020)
engineering = Node("Engineering", parent=root, budget=100000)
marketing = Node("Marketing", parent=root, budget=50000)
Node("Backend", parent=engineering, team_size=5)
Node("Frontend", parent=engineering, team_size=3)
# Basic dictionary export
exporter = DictExporter()
data = exporter.export(root)
print(data)
# {'children': [
# {'budget': 100000, 'children': [
# {'name': 'Backend', 'team_size': 5},
# {'name': 'Frontend', 'team_size': 3}
# ], 'name': 'Engineering'},
# {'budget': 50000, 'name': 'Marketing'}
# ], 'founded': 2020, 'name': 'Company'}
# Custom attribute filtering
def attr_filter(attrs):
# Only export specific attributes
return [(k, v) for k, v in attrs if k in ['name', 'budget']]
filtered_exporter = DictExporter(attriter=attr_filter)
filtered_data = filtered_exporter.export(root)
print(filtered_data) # Only name and budget attributes
# Limit export depth
shallow_exporter = DictExporter(maxlevel=2)
shallow_data = shallow_exporter.export(root)
print(f"Levels exported: {count_levels(shallow_data)}") # Max 2 levelsExport tree structures to JSON format for web APIs, configuration files, or data storage.
class JsonExporter:
"""
Export tree to JSON format.
Args:
dictcls: Dictionary class to use (default dict)
attriter: Function to filter/transform node attributes
childiter: Function to determine child iteration order (default list)
maxlevel: Maximum depth to export (default None - export all)
**kwargs: Additional arguments passed to json.dumps()
"""
def __init__(self, dictcls=dict, attriter=None, childiter=list, maxlevel=None, **kwargs): ...
def export(self, node): ...Usage Example:
from anytree import Node, JsonExporter
import json
# Create tree with various data types
root = Node("API", version="1.0")
users = Node("users", parent=root, endpoints=["GET", "POST", "PUT"])
posts = Node("posts", parent=root, endpoints=["GET", "POST"])
Node("profile", parent=users, auth_required=True)
Node("settings", parent=users, auth_required=True)
# Basic JSON export
exporter = JsonExporter()
json_data = exporter.export(root)
print(json_data) # JSON string
# Pretty printed JSON
pretty_exporter = JsonExporter(indent=2, sort_keys=True)
pretty_json = pretty_exporter.export(root)
print(pretty_json)
# Parse back to dict for verification
parsed = json.loads(json_data)
print(f"Root name: {parsed['name']}")
print(f"Users endpoints: {parsed['children'][0]['endpoints']}")Export tree structures to DOT format for visualization with Graphviz, creating professional tree diagrams and charts.
class DotExporter:
"""
Export tree to Graphviz DOT format.
Args:
node: Root node for export
graph: Graph type ("digraph" or "graph")
name: Graph name
options: Global graph options
indent: Indentation string
nodenamefunc: Function to generate node names
nodeattrfunc: Function to generate node attributes
edgeattrfunc: Function to generate edge attributes
edgetypefunc: Function to determine edge types
"""
def __init__(self, node, graph="digraph", name="tree", options=None, indent=" ",
nodenamefunc=None, nodeattrfunc=None, edgeattrfunc=None, edgetypefunc=None): ...
def export(self, node): ...
def to_dotfile(self, filename): ...
def to_picture(self, filename, **kwargs): ...Usage Example:
from anytree import Node, DotExporter
# Create organizational tree
root = Node("CEO", title="Chief Executive")
cto = Node("CTO", parent=root, title="Chief Technology Officer")
cfo = Node("CFO", parent=root, title="Chief Financial Officer")
Node("Backend Lead", parent=cto, title="Senior Engineer")
Node("Frontend Lead", parent=cto, title="Senior Engineer")
Node("Accountant", parent=cfo, title="Financial Analyst")
# Basic DOT export
exporter = DotExporter(root)
dot_output = exporter.export(root)
print(dot_output)
# Custom node attributes for styling
def node_attr(node):
attrs = [f'label="{node.name}\\n{getattr(node, "title", "")}"']
if node.name == "CEO":
attrs.append('shape=box')
attrs.append('style=filled')
attrs.append('fillcolor=lightblue')
return ", ".join(attrs)
styled_exporter = DotExporter(root, nodeattrfunc=node_attr)
styled_dot = styled_exporter.export(root)
# Save to file and generate image
styled_exporter.to_dotfile("org_chart.dot")
styled_exporter.to_picture("org_chart.png", format="png", prog="dot")DOT exporter that ensures unique node identifiers, useful for trees with duplicate node names.
class UniqueDotExporter(DotExporter):
"""
DOT exporter ensuring unique node identifiers.
Handles cases where multiple nodes have the same name.
"""
def __init__(self, node, **kwargs): ...Usage Example:
from anytree import Node, UniqueDotExporter
# Tree with duplicate names
root = Node("Department")
team1 = Node("Team", parent=root, location="Building A")
team2 = Node("Team", parent=root, location="Building B")
Node("Member", parent=team1, employee_id=101)
Node("Member", parent=team2, employee_id=102)
# Regular exporter might have issues with duplicate names
unique_exporter = UniqueDotExporter(root)
unique_dot = unique_exporter.export(root)
print(unique_dot) # Each node gets unique identifierExport tree structures to Mermaid diagram format for modern web-based visualization and documentation.
class MermaidExporter:
"""
Export tree to Mermaid diagram format.
Args:
indent: Indentation string
nodenamefunc: Function to generate node names
nodeattrfunc: Function to generate node attributes
edgeattrfunc: Function to generate edge attributes
"""
def __init__(self, indent=" ", nodenamefunc=None, nodeattrfunc=None, edgeattrfunc=None): ...
def export(self, node): ...Usage Example:
from anytree import Node, MermaidExporter
# Create process flow tree
root = Node("Start")
decision = Node("Decision", parent=root)
path_a = Node("Path A", parent=decision)
path_b = Node("Path B", parent=decision)
Node("End A", parent=path_a)
Node("End B", parent=path_b)
# Export to Mermaid format
exporter = MermaidExporter()
mermaid_output = exporter.export(root)
print(mermaid_output)
# graph TD
# Start --> Decision
# Decision --> "Path A"
# Decision --> "Path B"
# "Path A" --> "End A"
# "Path B" --> "End B"
# Custom node attributes
def node_attr(node):
if "End" in node.name:
return "shape=circle"
elif node.name == "Decision":
return "shape=diamond"
return ""
styled_exporter = MermaidExporter(nodeattrfunc=node_attr)
styled_mermaid = styled_exporter.export(root)Import tree structures from Python dictionary format, useful for deserializing exported data or creating trees from configuration files.
class DictImporter:
"""
Import tree from dictionary format.
Args:
nodecls: Node class to use for imported nodes (default AnyNode)
childiter: Attribute name for children list (default "children")
"""
def __init__(self, nodecls=AnyNode, childiter="children"): ...
def import_(self, data): ...Usage Example:
from anytree import DictImporter, RenderTree
# Dictionary data (e.g., from JSON file or API)
tree_data = {
"name": "Company",
"founded": 2020,
"children": [
{
"name": "Engineering",
"budget": 100000,
"children": [
{"name": "Backend", "team_size": 5},
{"name": "Frontend", "team_size": 3}
]
},
{
"name": "Marketing",
"budget": 50000,
"children": []
}
]
}
# Import dictionary to tree
importer = DictImporter()
root = importer.import_(tree_data)
print(RenderTree(root))
# AnyNode(founded=2020, name='Company')
# ├── AnyNode(budget=100000, name='Engineering')
# │ ├── AnyNode(name='Backend', team_size=5)
# │ └── AnyNode(name='Frontend', team_size=3)
# └── AnyNode(budget=50000, name='Marketing')
# Access imported attributes
print(f"Company founded: {root.founded}")
engineering = root.children[0]
print(f"Engineering budget: ${engineering.budget}")Import tree structures from JSON format, combining JSON parsing with dictionary import for direct JSON-to-tree conversion.
class JsonImporter:
"""
Import tree from JSON format.
Args:
nodecls: Node class to use for imported nodes (default AnyNode)
childiter: Attribute name for children list (default "children")
"""
def __init__(self, nodecls=AnyNode, childiter="children"): ...
def import_(self, data): ...Usage Example:
from anytree import JsonImporter, Node, RenderTree
import json
# JSON string (e.g., from file or API response)
json_data = '''
{
"name": "Project",
"version": "1.0.0",
"children": [
{
"name": "src",
"type": "directory",
"children": [
{"name": "main.py", "type": "file", "size": 1024},
{"name": "utils.py", "type": "file", "size": 512}
]
},
{
"name": "tests",
"type": "directory",
"children": [
{"name": "test_main.py", "type": "file", "size": 256}
]
}
]
}
'''
# Import JSON to tree
importer = JsonImporter()
root = importer.import_(json_data)
print(RenderTree(root))
# AnyNode(name='Project', version='1.0.0')
# ├── AnyNode(name='src', type='directory')
# │ ├── AnyNode(name='main.py', size=1024, type='file')
# │ └── AnyNode(name='utils.py', size=512, type='file')
# └── AnyNode(name='tests', type='directory')
# └── AnyNode(name='test_main.py', size=256, type='file')
# Custom node class
class FileNode(Node):
def __str__(self):
if self.type == "file":
return f"{self.name} ({self.size} bytes)"
return self.name
custom_importer = JsonImporter(nodecls=FileNode)
custom_root = custom_importer.import_(json_data)
print(RenderTree(custom_root))Export and re-import data to verify integrity:
from anytree import Node, DictExporter, DictImporter, RenderTree
# Original tree
original = Node("root")
Node("child1", parent=original, value=42)
Node("child2", parent=original, value=24)
print("Original:")
print(RenderTree(original))
# Export to dict
exporter = DictExporter()
data = exporter.export(original)
# Import back from dict
importer = DictImporter()
restored = importer.import_(data)
print("\nRestored:")
print(RenderTree(restored))
# Verify integrity
def tree_equals(node1, node2):
if node1.name != node2.name:
return False
if len(node1.children) != len(node2.children):
return False
return all(tree_equals(c1, c2) for c1, c2 in zip(node1.children, node2.children))
print(f"Trees equal: {tree_equals(original, restored)}")Control which attributes are exported and how they're processed:
from anytree import Node, DictExporter, DictImporter
class CustomNode(Node):
def __init__(self, name, **kwargs):
# Separate public and private attributes
super().__init__(name)
self.public_data = {}
self.private_data = {}
for key, value in kwargs.items():
if key.startswith('_'):
self.private_data[key] = value
else:
self.public_data[key] = value
# Create tree with mixed attributes
root = CustomNode("root", config="public", _secret="private")
child = CustomNode("child", parent=root, setting="visible", _internal="hidden")
# Export only public attributes
def public_attrs_only(attrs):
return [(k, v) for k, v in attrs if not k.startswith('_') and k != 'private_data']
exporter = DictExporter(attriter=public_attrs_only)
public_data = exporter.export(root)
print("Public data only:", public_data)Process multiple trees or large datasets efficiently:
from anytree import Node, JsonExporter, JsonImporter
import json
def process_tree_batch(tree_list, output_file):
"""Export multiple trees to single JSON file"""
exporter = JsonExporter()
batch_data = []
for tree in tree_list:
tree_data = json.loads(exporter.export(tree))
batch_data.append(tree_data)
with open(output_file, 'w') as f:
json.dump(batch_data, f, indent=2)
def import_tree_batch(input_file):
"""Import multiple trees from JSON file"""
importer = JsonImporter()
with open(input_file, 'r') as f:
batch_data = json.load(f)
trees = []
for tree_data in batch_data:
tree_json = json.dumps(tree_data)
tree = importer.import_(tree_json)
trees.append(tree)
return trees
# Example usage
trees = [
Node("Tree1", children=[Node("A"), Node("B")]),
Node("Tree2", children=[Node("X"), Node("Y")]),
]
process_tree_batch(trees, "trees.json")
restored_trees = import_tree_batch("trees.json")Handle different data formats and schemas:
from anytree import DictImporter, Node
class LegacyImporter(DictImporter):
"""Import from legacy format with different field names"""
def import_(self, data):
# Convert legacy format to standard format
converted = self._convert_legacy_format(data)
return super().import_(converted)
def _convert_legacy_format(self, node_data):
"""Convert old field names to new format"""
if isinstance(node_data, dict):
converted = {}
# Map old field names to new ones
field_mapping = {
'title': 'name',
'subnodes': 'children',
'properties': 'attributes'
}
for old_key, value in node_data.items():
new_key = field_mapping.get(old_key, old_key)
if new_key == 'children' and isinstance(value, list):
converted[new_key] = [self._convert_legacy_format(child) for child in value]
else:
converted[new_key] = value
return converted
return node_data
# Legacy data format
legacy_data = {
"title": "Old Root",
"properties": {"version": "1.0"},
"subnodes": [
{"title": "Old Child 1", "properties": {"type": "folder"}},
{"title": "Old Child 2", "properties": {"type": "file"}}
]
}
# Import using legacy converter
legacy_importer = LegacyImporter()
tree = legacy_importer.import_(legacy_data)
print(f"Imported root: {tree.name}") # "Old Root"Generate publication-quality diagrams:
from anytree import Node, DotExporter
import subprocess
def create_org_chart():
# Create organizational structure
ceo = Node("Alice Johnson", title="CEO", level="C-Level")
cto = Node("Bob Smith", parent=ceo, title="CTO", level="C-Level")
cfo = Node("Carol Davis", parent=ceo, title="CFO", level="C-Level")
# Engineering team
senior_eng = Node("David Wilson", parent=cto, title="Senior Engineer", level="Senior")
junior_eng = Node("Eve Brown", parent=cto, title="Junior Engineer", level="Junior")
# Finance team
accountant = Node("Frank Miller", parent=cfo, title="Accountant", level="Associate")
return ceo
def generate_styled_chart(root, filename):
def node_attrs(node):
attrs = [f'label="{node.name}\\n{node.title}"']
# Color by level
colors = {
"C-Level": "lightblue",
"Senior": "lightgreen",
"Junior": "lightyellow",
"Associate": "lightpink"
}
if hasattr(node, 'level'):
attrs.append(f'fillcolor={colors.get(node.level, "white")}')
attrs.append('style=filled')
return ", ".join(attrs)
exporter = DotExporter(root,
nodeattrfunc=node_attrs,
options=['rankdir=TB', 'node [shape=box]'])
# Generate PNG
exporter.to_picture(f"{filename}.png", format="png", prog="dot")
# Generate SVG for web use
exporter.to_picture(f"{filename}.svg", format="svg", prog="dot")
# Create and export chart
org_root = create_org_chart()
generate_styled_chart(org_root, "org_chart")Export for web-based visualization:
from anytree import Node, JsonExporter
import json
def create_interactive_data(root):
"""Prepare tree data for D3.js or similar libraries"""
def add_ui_attributes(attrs):
# Add attributes needed for interactive visualization
enhanced = list(attrs)
enhanced.extend([
('id', f"node_{hash(attrs)}"),
('collapsed', False),
('selected', False)
])
return enhanced
exporter = JsonExporter(attriter=add_ui_attributes, indent=2)
json_data = exporter.export(root)
# Wrap in format expected by visualization libraries
wrapped_data = {
"version": "1.0",
"tree": json.loads(json_data),
"metadata": {
"node_count": len(list(root.descendants)) + 1,
"max_depth": root.height,
"created": "2024-01-01T00:00:00Z"
}
}
return json.dumps(wrapped_data, indent=2)
# Generate interactive data
tree_root = Node("Interactive Root")
Node("Branch 1", parent=tree_root, category="primary")
Node("Branch 2", parent=tree_root, category="secondary")
interactive_json = create_interactive_data(tree_root)
print(interactive_json)Install with Tessl CLI
npx tessl i tessl/pypi-anytree