CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-anytree

Powerful and Lightweight Python Tree Data Structure with various plugins

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

import-export.mddocs/

Data Import and Export

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 Capabilities

DictExporter - Dictionary Export

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 levels

JsonExporter - JSON Export

Export 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']}")

DotExporter - Graphviz DOT Export

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")

UniqueDotExporter - Unique Node DOT Export

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 identifier

MermaidExporter - Mermaid Diagram Export

Export 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 Capabilities

DictImporter - Dictionary Import

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}")

JsonImporter - JSON Import

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))

Advanced Import/Export Patterns

Round-trip Conversion

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)}")

Custom Attribute Handling

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)

Batch Processing

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")

Legacy Format Support

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"

Visualization Integration

Graphviz Integration

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")

Web Integration

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

docs

import-export.md

index.md

node-construction.md

path-resolution.md

search.md

tree-iteration.md

tree-rendering.md

utilities.md

tile.json