treelib package

Module contents

treelib - Efficient Tree Data Structure for Python

treelib is a Python library providing a comprehensive tree data structure implementation. It features two primary classes: Node and Tree, designed for maximum efficiency and ease of use.

Key Features:
  • O(1) node access and search operations

  • Multiple tree traversal algorithms (DFS, BFS, ZigZag)

  • Rich tree visualization with customizable display formats

  • Flexible tree modification operations (add, remove, move, copy)

  • Data export/import (JSON, dictionary, GraphViz DOT)

  • Subtree operations and advanced filtering

  • Support for custom data payloads on nodes

  • Memory-efficient design with shallow/deep copy options

Architecture:
  • Tree: Self-contained hierarchical structure managing nodes and relationships

  • Node: Elementary objects storing data and maintaining parent-child links

  • Each tree has exactly one root node (or none if empty)

  • Each non-root node has exactly one parent and zero or more children

Quick Start:
from treelib import Tree, Node

# Create and populate a tree
tree = Tree()
tree.create_node("Root", "root")
tree.create_node("Child A", "a", parent="root")
tree.create_node("Child B", "b", parent="root")
tree.create_node("Grandchild", "a1", parent="a")

# Display the tree
tree.show()

# Tree traversal
for node_id in tree.expand_tree(mode=Tree.DEPTH):
    print(f"Visiting: {tree[node_id].tag}")

# Export to JSON
json_data = tree.to_json()
Common Use Cases:
  • File system representations

  • Organizational hierarchies

  • Decision trees and game trees

  • Category/taxonomy structures

  • Menu and navigation systems

  • Abstract syntax trees

  • Family trees and genealogy

Compatibility Note:

To ensure string compatibility between Python 2.x and 3.x, treelib follows Python 3.x string handling conventions. All strings are handled as unicode.

For Python 2.x users with non-ASCII characters, enable unicode literals:

from __future__ import unicode_literals

Submodules

treelib.node module

Node structure in treelib.

A Node object contains basic properties such as node identifier, node tag, parent node, children nodes etc., and some operations for a node.

The Node class is the fundamental building block of a Tree. Each node can store: - A unique identifier for tree operations - A human-readable tag for display - Custom data payload - Parent-child relationships within one or more trees

Note:

Nodes are typically created through Tree.create_node() rather than directly instantiated, as the Tree class manages the parent-child relationships automatically.

class treelib.node.Node(tag: str | None = None, identifier: str | None = None, expanded: bool = True, data: Any = None)[source]

Bases: object

Elementary node object stored in Tree structures.

A Node represents a single element in a tree hierarchy. Each node maintains references to its parent and children within specific tree contexts, along with a human-readable tag and optional data payload.

Attributes:

identifier (str): Unique identifier for this node within a tree. tag (str): Human-readable label for display purposes. expanded (bool): Controls visibility of children in tree displays. data (Any): User-defined payload associated with this node.

Example:

Creating nodes with different configurations:

# Basic node
node = Node("Root", "root")

# Node with custom data
node = Node("File", "file1", data={"size": 1024, "type": "txt"})

# Node that starts collapsed
node = Node("Folder", "folder1", expanded=False)
Warning:

While nodes can be created directly, it’s recommended to use Tree.create_node() which properly manages tree relationships.

ADD = 0

Mode constants for routine update_fpointer().

DELETE = 1

Mode constants for routine update_fpointer().

INSERT = 2

Mode constants for routine update_fpointer().

REPLACE = 3

Mode constants for routine update_fpointer().

property bpointer
clone_pointers(former_tree_id: str, new_tree_id: str) None[source]

Clone parent-child relationships from one tree context to another.

Copies the complete relationship structure (parent and children) from one tree context to a new tree context. Used when moving or copying nodes between trees while preserving their hierarchical relationships.

Args:

former_tree_id: Source tree identifier to copy relationships from. new_tree_id: Target tree identifier to copy relationships to.

Example:

Cloning relationships between trees:

# Copy node relationships from tree1 to tree2
node.clone_pointers("tree1", "tree2")

# Now node has same relationships in both trees
assert node.predecessor("tree1") == node.predecessor("tree2")
data

User payload associated with this node.

expanded

boolean

property fpointer
property identifier: str

Get the unique identifier of this node.

The identifier serves as the primary key for this node within tree structures. It must be unique within each tree that contains this node. Used for all tree operations including lookup, traversal, and relationship management.

Returns:

str: The unique node identifier.

Example:

Accessing node identifier:

node = Node("My Node", "unique_id")
print(node.identifier)  # "unique_id"

# Use in tree operations
tree[node.identifier]  # Access via identifier
is_leaf(tree_id: str | None = None) bool[source]

Check if this node is a leaf (has no children) in the specified tree.

A leaf node is one that has no child nodes. This is a fundamental property used in tree algorithms and can vary between different tree contexts if the node exists in multiple trees.

Args:
tree_id: Identifier of the tree to check leaf status in.

If None, uses the initial tree for backward compatibility.

Returns:

bool: True if node has no children in the specified tree, False otherwise.

Example:

Checking leaf status:

if node.is_leaf("tree1"):
    print("This is a leaf node")

# Different trees may have different leaf status
is_leaf_tree1 = node.is_leaf("tree1")
is_leaf_tree2 = node.is_leaf("tree2")  # May differ
is_root(tree_id: str | None = None) bool[source]

Check if this node is a root (has no parent) in the specified tree.

A root node is the topmost node in a tree hierarchy with no parent. Every tree has exactly one root node. This status can vary between different tree contexts if the node exists in multiple trees.

Args:
tree_id: Identifier of the tree to check root status in.

If None, uses the initial tree for backward compatibility.

Returns:

bool: True if node has no parent in the specified tree, False otherwise.

Example:

Checking root status:

if node.is_root("tree1"):
    print("This is the root node")

# Same node might be root in one tree but not another
is_root_tree1 = node.is_root("tree1")
is_root_tree2 = node.is_root("tree2")  # May differ
predecessor(tree_id)[source]

Get the parent node identifier in the specified tree.

Since nodes can exist in multiple trees simultaneously, this method returns the parent identifier for this node within a specific tree context.

Args:

tree_id: Identifier of the tree to query parent relationship in.

Returns:

str or None: Parent node identifier, or None if this is a root node.

Example:

Accessing parent in different trees:

# Node can have different parents in different trees
parent_id = node.predecessor("tree1")
if parent_id:
    print(f"Parent in tree1: {parent_id}")
reset_pointers(tree_id) None[source]

Reset all parent-child relationships for a specific tree context.

Clears both parent and children references for this node within the specified tree, effectively making it an isolated node. Used during tree restructuring operations.

Args:

tree_id: Identifier of the tree to reset relationships in.

Example:

Resetting node relationships:

# Clear all relationships in tree1
node.reset_pointers("tree1")

# Node now has no parent or children in tree1
assert node.predecessor("tree1") is None
assert len(node.successors("tree1")) == 0
set_initial_tree_id(tree_id: str) None[source]

Set the initial tree identifier for backward compatibility.

This method is used internally to maintain compatibility with older node pointer systems. Each node remembers the first tree it was added to for legacy bpointer/fpointer access.

Args:

tree_id: Unique identifier of the tree this node was first added to.

Note:

This is an internal method used for legacy compatibility. Modern code should use the tree-specific pointer methods instead.

set_predecessor(nid: str | None, tree_id: str | None) None[source]

Set the parent node identifier for this node in a specific tree.

Establishes or updates the parent-child relationship for this node within the context of a particular tree. Used internally by tree operations to maintain proper hierarchy.

Args:

nid: Parent node identifier, or None to make this a root node. tree_id: Identifier of the tree to set parent relationship in.

Example:

Setting parent relationship:

node.set_predecessor("parent_id", "tree1")
node.set_predecessor(None, "tree1")  # Make root
set_successors(value: None | list | dict | set, tree_id: str | None = None) None[source]

Set the complete list of child node identifiers for a specific tree.

Replaces the entire children list with new values. Accepts multiple input formats for flexibility: list, set, dict (uses keys), or None.

Args:
value: New children collection. Can be:
  • list: Used directly as child identifiers

  • set: Converted to list of identifiers

  • dict: Uses dictionary keys as identifiers

  • None: Sets empty children list

tree_id: Identifier of the tree to set children in.

Raises:

NotImplementedError: If value type is not supported.

Example:

Setting children from different sources:

# From list
node.set_successors(["child1", "child2"], "tree1")

# From set
node.set_successors({"child1", "child2"}, "tree1")

# From dict (uses keys)
node.set_successors({"child1": data1, "child2": data2}, "tree1")

# Clear children
node.set_successors(None, "tree1")
successors(tree_id: str | None) list[str][source]

Get the list of child node identifiers in the specified tree.

Returns all direct children of this node within a specific tree context. Children are maintained as an ordered list to preserve insertion order and support custom sorting.

Args:

tree_id: Identifier of the tree to query children in.

Returns:

list[str]: List of child node identifiers. Empty list if no children.

Example:

Accessing children in different trees:

children = node.successors("tree1")
for child_id in children:
    print(f"Child: {child_id}")

# Node can have different children in different trees
tree1_children = node.successors("tree1")
tree2_children = node.successors("tree2")
property tag: str

Get the human-readable display name of this node.

The tag serves as the display label for this node in tree visualizations, printouts, and user interfaces. Unlike the identifier, the tag doesn’t need to be unique and is primarily for human readability.

Returns:

str: The display name/label for this node.

Example:

Using node tags for display:

node = Node("User Profile", "user123")
print(node.tag)        # "User Profile"
print(node.identifier) # "user123"

# Tags are used in tree display
tree.show()  # Shows "User Profile" not "user123"
update_bpointer(nid) None[source]
update_fpointer(nid, mode=0, replace=None)[source]
update_successors(nid: str | None, mode: int = 0, replace: str | None = None, tree_id: str | None = None) None[source]

Update the children list with different modification modes.

Provides granular control over child node list modifications without replacing the entire list. Supports adding, removing, and replacing individual child references.

Args:

nid: Child node identifier to operate on. mode: Operation type using Node constants:

  • Node.ADD: Append child to end of list

  • Node.DELETE: Remove child from list

  • Node.INSERT: Deprecated, same as ADD

  • Node.REPLACE: Replace child with another identifier

replace: New identifier when mode=REPLACE. Required for replace mode. tree_id: Identifier of the tree to modify children in.

Raises:

NodePropertyError: If replace is None when mode=REPLACE. NotImplementedError: If mode is not supported.

Example:

Different update operations:

# Add a child
node.update_successors("new_child", Node.ADD, tree_id="tree1")

# Remove a child
node.update_successors("old_child", Node.DELETE, tree_id="tree1")

# Replace a child
node.update_successors("old_child", Node.REPLACE,
                     replace="new_child", tree_id="tree1")

treelib.tree module

Tree structure in treelib.

The Tree object defines the tree-like structure based on Node objects. Trees provide hierarchical data organization with efficient operations for traversal, modification, search, and visualization.

A new tree can be created from scratch without any parameter or a shallow/deep copy of another tree. When deep=True, a deepcopy operation is performed on feeding tree parameter and more memory is required to create the tree.

Key Features:
  • O(1) node lookup and access

  • Multiple traversal modes (depth-first, breadth-first, zigzag)

  • Flexible tree modification (add, remove, move, paste)

  • Rich display options with customizable formatting

  • JSON/dict export and import capabilities

  • Subtree operations and filtering

  • Tree metrics and analysis tools

class treelib.tree.Tree(tree=None, deep: bool = False, node_class=None, identifier: str | None = None)[source]

Bases: object

Hierarchical tree data structure.

A Tree is a collection of Node objects organized in a hierarchical structure with exactly one root node and zero or more child nodes. Each node (except root) has exactly one parent, but can have multiple children.

The tree provides efficient operations for: - Adding, removing, and moving nodes - Traversing the tree in different orders - Searching and filtering nodes - Displaying tree structure - Exporting to various formats

Attributes:

root (str): Identifier of the root node, or None if tree is empty. nodes (dict): Dictionary mapping node identifiers to Node objects.

Constants:

ROOT (int): Tree traversal starting from root level. DEPTH (int): Depth-first tree traversal mode. WIDTH (int): Breadth-first tree traversal mode. ZIGZAG (int): Zigzag tree traversal mode.

Example:

Creating and manipulating a tree:

tree = Tree()

# Build structure
tree.create_node("Company", "company")
tree.create_node("Engineering", "eng", parent="company")
tree.create_node("Sales", "sales", parent="company")

# Add team members
tree.create_node("Alice", "alice", parent="eng")
tree.create_node("Bob", "bob", parent="eng")
tree.create_node("Carol", "carol", parent="sales")

# Display tree
tree.show()

# Find specific nodes
eng_team = tree.children("eng")
all_employees = [tree[node].tag for node in tree.expand_tree()
               if tree.level(node) > 1]

# Tree operations
tree.move_node("alice", "sales")  # Alice moves to sales
subtree = tree.subtree("eng")     # Get engineering subtree
DEPTH = 1

ROOT, DEPTH, WIDTH, ZIGZAG constants :

ROOT = 0

ROOT, DEPTH, WIDTH, ZIGZAG constants :

WIDTH = 2

ROOT, DEPTH, WIDTH, ZIGZAG constants :

ZIGZAG = 3

ROOT, DEPTH, WIDTH, ZIGZAG constants :

add_node(node: Node, parent: Node | str | None = None) None[source]

Add an existing node object to the tree.

Integrates a pre-created Node instance into the tree structure by establishing parent-child relationships and updating internal mappings. For creating and adding nodes simultaneously, use create_node() instead.

Args:
node: Existing Node instance to add to the tree.

Must be an instance of the tree’s node_class.

parent: Parent node (Node object or identifier string), or None

to make this node the root. If None, tree must be empty.

Raises:

OSError: If node is not an instance of the expected node class. DuplicatedNodeIdError: If node identifier already exists in tree. MultipleRootError: If parent is None but tree already has a root. NodeIDAbsentError: If parent identifier doesn’t exist in tree.

Example:

Adding pre-created nodes:

# Create nodes first
root_node = Node("Company", "company")
dept_node = Node("Engineering", "eng")

# Add to tree
tree.add_node(root_node)  # Root node
tree.add_node(dept_node, parent="company")  # Child node

# Adding with Node object as parent
mgr_node = Node("Manager", "mgr")
tree.add_node(mgr_node, parent=dept_node)
all_nodes() list[Node][source]

Get all nodes in the tree as a list.

Returns a list containing all Node objects currently in the tree. The order is not guaranteed. For ordered traversal, use expand_tree(). Useful for operations that need to process all nodes simultaneously.

Returns:

list[Node]: List of all Node objects in the tree.

Example:

Processing all nodes:

# Get all nodes
all_nodes = tree.all_nodes()
print(f"Total nodes: {len(all_nodes)}")

# Process each node
for node in all_nodes:
    print(f"Node: {node.tag}")

# Filter nodes by property
leaf_nodes = [node for node in tree.all_nodes()
             if node.is_leaf(tree.identifier)]
all_nodes_itr() Any[source]

Get all nodes in the tree as an iterator.

Returns an iterator over all Node objects in the tree, providing memory-efficient access for large trees. Useful when you need to process nodes one at a time without loading all into memory.

Returns:

Iterator[Node]: Iterator over all Node objects in the tree.

Example:

Memory-efficient node processing:

# Iterate without loading all nodes
for node in tree.all_nodes_itr():
    if node.tag.startswith("temp_"):
        process_temporary_node(node)

# Use with filter operations
filtered = filter(lambda n: n.data is not None,
                tree.all_nodes_itr())
Note:

Added by William Rusnack for memory efficiency.

ancestor(nid, level=None)[source]

Get the ancestor node at a specific level above the given node.

Traverses up the tree hierarchy from the specified node to find an ancestor at the desired level. If no level is specified, returns the immediate parent. Useful for navigating tree hierarchies.

Args:

nid: Identifier of the node to start from. level: Target level of the ancestor (0 = root). If None,

returns immediate parent.

Returns:

str or None: Identifier of the ancestor node, or None if not found.

Raises:

NodeIDAbsentError: If nid doesn’t exist in the tree. InvalidLevelNumber: If level is invalid (>= descendant level).

Example:

Finding ancestors:

# Get immediate parent
parent_id = tree.ancestor("grandchild")

# Get ancestor at specific level
root_id = tree.ancestor("grandchild", level=0)  # Root
grandparent_id = tree.ancestor("grandchild", level=1)

# Use in hierarchy navigation
if tree.ancestor("node", level=0) == "root":
    print("Node is in main hierarchy")
children(nid: str) list[Node][source]

Get all direct children of the specified node.

Returns a list of Node objects that are immediate children of the given node. The order follows the insertion order unless the tree has been sorted. Returns empty list if node has no children.

Args:

nid: Identifier of the parent node.

Returns:

list[Node]: List of child Node objects. Empty if no children.

Raises:

NodeIDAbsentError: If nid doesn’t exist in the tree.

Example:

Working with child nodes:

# Get all children
child_nodes = tree.children("parent_id")
print(f"Parent has {len(child_nodes)} children")

# Process each child
for child in tree.children("parent_id"):
    print(f"Child: {child.tag}")

# Check if node has children
if tree.children("node_id"):
    print("Node has children")
else:
    print("Node is a leaf")
contains(nid)[source]

Check if the tree contains a node with the given identifier.

Determines whether a node with the specified identifier exists in this tree. Equivalent to using the ‘in’ operator but provided as an explicit method for clarity and consistency.

Args:

nid: Node identifier to check for existence.

Returns:

bool: True if node exists in tree, False otherwise.

Example:

Checking node existence:

# Explicit method call
if tree.contains("user123"):
    print("User node exists")

# Equivalent using 'in' operator
if "user123" in tree:
    print("User node exists")

# Use before operations
if tree.contains("node_id"):
    tree.move_node("node_id", "new_parent")
create_node(tag: str | None = None, identifier: str | None = None, parent: Node | str | None = None, data: Any = None) Node[source]

Create and add a new node to the tree.

This is the primary method for building tree structures. Creates a new node with the specified properties and attaches it to the given parent. If no parent is specified, the node becomes the root (only allowed if tree is empty).

Args:

tag: Human-readable label for display. If None, uses identifier. identifier: Unique ID for the node. If None, generates UUID automatically. parent: Parent node identifier, Node object, or None for root.

Must be None if tree is empty, must exist if tree has nodes.

data: Optional user data to associate with this node.

Returns:

Node: The newly created Node object.

Raises:

DuplicatedNodeIdError: If identifier already exists in the tree. MultipleRootError: If parent is None but tree already has a root. NodeIDAbsentError: If parent identifier doesn’t exist in the tree.

Example:

Building a family tree:

tree = Tree()

# Create root (no parent)
tree.create_node("Grandpa", "grandpa")

# Add children
tree.create_node("Dad", "dad", parent="grandpa")
tree.create_node("Uncle", "uncle", parent="grandpa")

# Add grandchildren
tree.create_node("Me", "me", parent="dad")
tree.create_node("Sister", "sister", parent="dad")

# Add node with custom data
tree.create_node("Baby", "baby", parent="me",
               data={"age": 1, "cute": True})
depth(node: Node | None = None) int[source]

Get the maximum depth of the tree or the level of a specific node.

When called without arguments, returns the maximum depth of the entire tree (distance from root to deepest leaf). When called with a node, returns the level of that specific node (distance from root).

Args:
node: Node object or identifier string. If None, returns tree depth.

If provided, returns the level of this specific node.

Returns:

int: Tree depth (max level) or node level. Root is at level 0.

Raises:

NodeIDAbsentError: If specified node doesn’t exist in the tree.

Example:

Measuring tree dimensions:

# Get overall tree depth
max_depth = tree.depth()
print(f"Tree depth: {max_depth}")

# Get specific node level
node_level = tree.depth("some_node")
node_level2 = tree.depth(tree["some_node"])  # Same result

# Use for tree analysis
if tree.depth() > 5:
    print("Tree is quite deep")
expand_tree(nid: str | None = None, mode: int = 1, filter: Callable[[Node], bool] | None = None, key: Callable[[Node], Node] | None = None, reverse: bool = False, sorting: bool = True)[source]

Traverse the tree and yield node identifiers in specified order.

This is the primary method for tree iteration, providing flexible traversal with multiple algorithms, filtering, and sorting options. Essential for most tree processing operations.

Args:
nid: Starting node identifier. If None, starts from tree root.

Must exist in the tree if specified.

mode: Traversal algorithm to use:

Tree.DEPTH (0) - Depth-first search (default) Tree.WIDTH (1) - Breadth-first search Tree.ZIGZAG (2) - ZigZag traversal (alternating levels)

filter: Optional function to filter nodes during traversal.

Takes Node object, returns bool. If False, node and its entire subtree are skipped.

key: Sorting function for nodes at each level. Takes Node object,

returns comparison key. If None and sorting=True, sorts by node.

reverse: If True, reverse the sorting order at each level. sorting: If True, sort nodes at each level using key function.

If False, preserve insertion order (key/reverse ignored).

Yields:

str: Node identifiers in traversal order.

Raises:

NodeIDAbsentError: If nid doesn’t exist in the tree. ValueError: If mode is not a valid traversal constant.

Example:

Different traversal patterns:

tree = Tree()
tree.create_node("A", "a")
tree.create_node("B", "b", parent="a")
tree.create_node("C", "c", parent="a")
tree.create_node("D", "d", parent="b")
tree.create_node("E", "e", parent="c")

# Depth-first (default): A, B, D, C, E
for node_id in tree.expand_tree():
    print(f"DFS: {tree[node_id].tag}")

# Breadth-first: A, B, C, D, E
for node_id in tree.expand_tree(mode=Tree.WIDTH):
    print(f"BFS: {tree[node_id].tag}")

# ZigZag traversal
for node_id in tree.expand_tree(mode=Tree.ZIGZAG):
    print(f"ZigZag: {tree[node_id].tag}")

# Filtered traversal (only nodes with vowels)
vowel_filter = lambda node: any(v in node.tag.lower() for v in 'aeiou')
for node_id in tree.expand_tree(filter=vowel_filter):
    print(f"Vowels: {tree[node_id].tag}")

# Sorted by tag, reversed
for node_id in tree.expand_tree(key=lambda x: x.tag, reverse=True):
    print(f"Sorted: {tree[node_id].tag}")

# From specific subtree
for node_id in tree.expand_tree(nid="b"):
    print(f"Subtree: {tree[node_id].tag}")

Common Usage Patterns:

# Process all nodes
for node_id in tree.expand_tree():
    process_node(tree[node_id])

# Get all node tags
tags = [tree[nid].tag for nid in tree.expand_tree()]

# Find specific nodes
matching_nodes = [nid for nid in tree.expand_tree()
                 if tree[nid].tag.startswith("prefix")]

# Level-order processing
for node_id in tree.expand_tree(mode=Tree.WIDTH):
    level = tree.level(node_id)
    print(f"Level {level}: {tree[node_id].tag}")
Performance:
  • Time complexity: O(n) where n is number of visited nodes

  • Memory: O(h) where h is tree height (for traversal stack)

  • Generator-based: memory efficient for large trees

Note:

This is a generator function - it yields results lazily. Perfect for large trees where you might not need to visit all nodes, or when memory efficiency is important.

filter_nodes(func: Callable[[Node], bool])[source]

Filter all nodes in the tree using a custom function.

Applies the given function to every node and returns an iterator over nodes where the function returns True. Useful for finding specific types of nodes or nodes with certain properties.

Args:
func: Function that takes a Node object and returns bool.

True means include the node in results.

Returns:

Iterator[Node]: Iterator over nodes where func returns True.

Example:

Filtering nodes by criteria:

# Find all leaf nodes
leaves = list(tree.filter_nodes(lambda n: n.is_leaf(tree.identifier)))

# Find nodes with specific data
important_nodes = list(tree.filter_nodes(
    lambda n: hasattr(n.data, 'priority') and n.data.priority == 'high'
))

# Find nodes by tag pattern
temp_nodes = list(tree.filter_nodes(
    lambda n: n.tag.startswith('temp_')
))

# Memory efficient processing
for node in tree.filter_nodes(lambda n: n.data is not None):
    process_node_with_data(node)
Note:

Added by William Rusnack for flexible node filtering.

classmethod from_map(child_parent_dict, id_func=None, data_func=None)[source]

takes a dict with child:parent, and form a tree

get_node(nid: str | None) Node | None[source]

Safely get a node by identifier without raising exceptions.

Provides null-safe access to nodes, returning None if the node doesn’t exist instead of raising an exception. Preferred over bracket notation when node existence is uncertain.

Args:

nid: Node identifier to retrieve, or None.

Returns:

Node or None: The node object if found, None otherwise.

Example:

Safe node access:

# Safe access (no exception)
node = tree.get_node("might_not_exist")
if node is not None:
    print(f"Found: {node.tag}")
else:
    print("Node not found")

# Compare with bracket notation
try:
    node = tree["might_not_exist"]  # Raises exception
except NodeIDAbsentError:
    node = None

# Use in conditional logic
user_node = tree.get_node("user123")
if user_node and user_node.data.active:
    process_active_user(user_node)
property identifier: str | None

Get the unique identifier of this tree instance.

Each tree has its own unique identifier used to distinguish it from other trees, especially when nodes exist in multiple trees simultaneously. This identifier is automatically generated if not provided during creation.

Returns:

str or None: The unique identifier of this tree instance.

Example:

Using tree identifiers:

tree1 = Tree(identifier="main_tree")
tree2 = Tree()  # Auto-generated identifier

print(tree1.identifier)  # "main_tree"
print(tree2.identifier)  # UUID string like "abc123..."

# Used internally for node relationships
node.predecessor(tree1.identifier)  # Parent in tree1
is_ancestor(ancestor, grandchild)[source]

Check if one node is an ancestor of another node.

Determines whether the ancestor node appears in the path from the grandchild node to the root. An ancestor is any node that appears above another node in the tree hierarchy.

Args:

ancestor: Identifier of the potential ancestor node. grandchild: Identifier of the potential descendant node.

Returns:
bool: True if ancestor is indeed an ancestor of grandchild,

False otherwise.

Example:

Checking ancestry relationships:

# Direct parent-child
is_parent = tree.is_ancestor("parent", "child")  # True

# Multi-level ancestry
is_grandparent = tree.is_ancestor("grandparent", "grandchild")  # True

# Not related
is_ancestor = tree.is_ancestor("sibling1", "sibling2")  # False

# Prevent circular moves
if not tree.is_ancestor("node_to_move", "new_parent"):
    tree.move_node("node_to_move", "new_parent")
else:
    print("Cannot create circular reference")
is_branch(nid)[source]

Get the list of child node identifiers for the specified node.

Returns the identifiers of all direct children of the given node. Despite the name suggesting a boolean, this method actually returns the list of child identifiers. Use children() for Node objects.

Args:

nid: Identifier of the parent node. Cannot be None.

Returns:

list[str]: List of child node identifiers. Empty if no children.

Raises:

OSError: If nid is None. NodeIDAbsentError: If nid doesn’t exist in the tree.

Example:

Getting child identifiers:

# Get child IDs
child_ids = tree.is_branch("parent_id")
for child_id in child_ids:
    print(f"Child ID: {child_id}")

# Check if node has children
if tree.is_branch("node_id"):
    print("Node has children")

# Use with node objects
child_nodes = [tree[child_id] for child_id in tree.is_branch("parent")]
leaves(nid: str | None = None) list[Node][source]

Get all leaf nodes in the tree or subtree.

Returns all nodes that have no children. If a starting node is specified, only considers leaves within that subtree. Leaf nodes are terminal nodes in the tree structure and often represent end points in hierarchies.

Args:

nid: Root of subtree to search. If None, searches entire tree.

Returns:

list[Node]: List of all leaf Node objects in the specified scope.

Example:

Finding leaf nodes:

# Get all leaves in tree
all_leaves = tree.leaves()
print(f"Tree has {len(all_leaves)} leaf nodes")

# Get leaves in specific subtree
subtree_leaves = tree.leaves("department_root")

# Process leaf nodes
for leaf in tree.leaves():
    print(f"Leaf: {leaf.tag}")

# Find deepest nodes
max_level = max(tree.level(leaf.identifier) for leaf in tree.leaves())
deepest_leaves = [leaf for leaf in tree.leaves()
                if tree.level(leaf.identifier) == max_level]
level(nid, filter=None)[source]

Get the level (depth) of a node in the tree hierarchy.

Returns the distance from the root to the specified node, where the root is at level 0. Optionally filters the path calculation by excluding certain nodes.

Args:

nid: Identifier of the node to get level for. filter: Optional function to filter nodes during path calculation.

Takes Node object, returns bool. Filtered nodes are excluded.

Returns:

int: Level of the node (0 for root, 1 for root’s children, etc.)

Example:

Getting node levels:

# Basic level calculation
root_level = tree.level("root")        # 0
child_level = tree.level("child")      # 1
grandchild_level = tree.level("gc")    # 2

# Use for tree analysis
max_depth = max(tree.level(node.identifier)
              for node in tree.all_nodes())

# Filtered level calculation
level = tree.level("node", filter=lambda n: n.tag != "skip")

Remove a node while preserving connections between its parent and children.

Deletes the specified node but maintains the tree structure by directly connecting the node’s parent to all of its children. This operation effectively “links past” the node, removing it from the hierarchy without breaking the tree connections.

Args:

nid: Identifier of the node to link past and remove.

Raises:

NodeIDAbsentError: If nid doesn’t exist in the tree. LinkPastRootNodeError: If attempting to link past the root node.

Example:

Linking past nodes:

# Before: Root -> Manager -> Employee1, Employee2
tree.link_past_node("Manager")
# After: Root -> Employee1, Employee2

# Useful for flattening hierarchies
middle_managers = ["mgr1", "mgr2", "mgr3"]
for mgr in middle_managers:
    if tree.contains(mgr):
        tree.link_past_node(mgr)

# Cannot link past root
try:
    tree.link_past_node(tree.root)
except LinkPastRootNodeError:
    print("Cannot link past root node")
merge(nid: str, new_tree, deep: bool = False)[source]

Patch @new_tree on current tree by pasting new_tree root children on current tree @nid node.

Consider the following tree: >>> current.show() root ├── A └── B >>> new_tree.show() root2 ├── C └── D

└── D1

Merging new_tree on B node: >>>current.merge(‘B’, new_tree) >>>current.show() root ├── A └── B

├── C └── D

└── D1

Note: if current tree is empty and nid is None, the new_tree root will be used as root on current tree. In all other cases new_tree root is not pasted.

move_node(source, destination)[source]

Move a node from its current parent to a new parent.

Changes the parent-child relationship by moving the source node (and its entire subtree) from its current parent to the specified destination parent. This operation preserves the subtree structure while reorganizing the tree hierarchy.

Args:

source: Identifier of the node to move. destination: Identifier of the new parent node.

Raises:

NodeIDAbsentError: If source or destination node doesn’t exist. LoopError: If moving would create a circular reference

(destination is a descendant of source).

Example:

Reorganizing tree structure:

# Move employee between departments
tree.move_node("employee123", "sales_dept")

# Move entire department
tree.move_node("engineering_dept", "new_division")

# Prevent circular moves (this would raise LoopError)
try:
    tree.move_node("parent", "child_of_parent")
except LoopError:
    print("Cannot create circular reference")

# Bulk reorganization
employees = ["emp1", "emp2", "emp3"]
for emp in employees:
    tree.move_node(emp, "new_manager")
node_class

alias of Node

property nodes

Get the internal dictionary mapping node identifiers to Node objects.

Returns the underlying dictionary that stores all nodes in the tree, mapping from node identifiers to Node instances. Useful for direct access to the node collection and bulk operations.

Returns:

dict[str, Node]: Dictionary mapping node IDs to Node objects.

Example:

Working with the nodes dictionary:

# Get all node identifiers
all_ids = list(tree.nodes.keys())

# Get all node objects
all_nodes = list(tree.nodes.values())

# Direct access to nodes dict
nodes_dict = tree.nodes
for node_id, node in nodes_dict.items():
    print(f"ID: {node_id}, Tag: {node.tag}")

# Bulk operations
total_nodes = len(tree.nodes)
has_node = "some_id" in tree.nodes
parent(nid: str) Node | None[source]

Get the parent Node object of the specified node.

Returns the immediate parent of the given node, or None if the node is the root or has no parent. Provides object-based access to the parent, unlike ancestor() which returns identifiers.

Args:

nid: Identifier of the node whose parent to retrieve.

Returns:

Node or None: Parent Node object, or None if node is root.

Raises:

NodeIDAbsentError: If nid doesn’t exist in the tree.

Example:

Working with parent relationships:

# Get parent node
parent_node = tree.parent("child_id")
if parent_node:
    print(f"Parent: {parent_node.tag}")
else:
    print("Node is root or orphaned")

# Navigate up the hierarchy
current = "leaf_node"
while tree.parent(current):
    current = tree.parent(current).identifier
    print(f"Ancestor: {tree[current].tag}")

# Check parent properties
parent = tree.parent("employee")
if parent and hasattr(parent.data, 'department'):
    print(f"Department: {parent.data.department}")
paste(nid: str, new_tree, deep: bool = False)[source]

Paste a @new_tree to the original one by linking the root of new tree to given node (nid).

Update: add @deep copy of pasted tree.

paths_to_leaves() list[list[str]][source]

Use this function to get the identifiers allowing to go from the root nodes to each leaf.

Returns:

a list of list of identifiers, root being not omitted.

For example:

Harry
|___ Bill
|___ Jane
|    |___ Diane
|         |___ George
|              |___ Jill
|         |___ Mary
|    |___ Mark

Expected result:

[['harry', 'jane', 'diane', 'mary'],
 ['harry', 'jane', 'mark'],
 ['harry', 'jane', 'diane', 'george', 'jill'],
 ['harry', 'bill']]
remove_node(identifier: str) int[source]

Remove a node indicated by ‘identifier’ with all its successors. Return the number of removed nodes.

remove_subtree(nid: str, identifier: str | None = None)[source]

Get a subtree with nid being the root. If nid is None, an empty tree is returned.

For the original tree, this method is similar to remove_node(self,nid), because given node and its children are removed from the original tree in both methods. For the returned value and performance, these two methods are different:

  • remove_node returns the number of deleted nodes;

  • remove_subtree returns a subtree of deleted nodes;

You are always suggested to use remove_node if your only to delete nodes from a tree, as the other one need memory allocation to store the new tree.

Returns:

a Tree object.

root: str | None

Get or set the identifier of the root. This attribute can be accessed and modified with . and = operator respectively.

rsearch(nid: str, filter: Callable[[Node], bool] | None = None)[source]

Traverse the tree branch along the branch from nid to its ancestors (until root).

Parameters:

filter – the function of one variable to act on the Node object.

save2file(filename: str, nid: str | None = None, level: int = 0, idhidden: bool = True, filter: Callable[[Node], bool] | None = None, key: Callable[[Node], Node] | None = None, reverse: bool = False, line_type: str = 'ascii-ex', data_property: str | None = None, sorting: bool = True)[source]

Save the tree into file for offline analysis.

show(nid: str | None = None, level: int = 0, idhidden: bool = True, filter: Callable[[Node], bool] | None = None, key: Callable[[Node], Node] | None = None, reverse: bool = False, line_type: str = 'ascii-ex', data_property: str | None = None, stdout: bool = True, sorting: bool = True)[source]

Display the tree structure in a beautiful hierarchical format.

This method provides rich visualization options for tree structures, allowing customization of appearance, content filtering, and display properties. Perfect for debugging, documentation, and user interfaces.

Args:

nid: Starting node identifier. If None, starts from root. level: Starting level (0 for root). Mainly for internal use. idhidden: If True, hide node identifiers in output (cleaner display).

If False, show both tag and identifier as “tag[id]”.

filter: Function to selectively display nodes. Takes Node object,

returns bool. Filtered nodes and their subtrees are hidden.

key: Sorting function for nodes at each level. Takes Node object,

returns comparison key. If None and sorting=True, sorts by Node.

reverse: If True, reverse the sorting order at each level. line_type: Visual style for tree connections. Options:

‘ascii’ - Simple |– style ‘ascii-ex’ - Unicode ├── style (default) ‘ascii-exr’ - Unicode with rounded corners ‘ascii-em’ - Double-line Unicode ╠══ style ‘ascii-emv’ - Mixed vertical Unicode style ‘ascii-emh’ - Mixed horizontal Unicode style

data_property: If specified, display this property from node.data

instead of node.tag. Useful for rich data objects.

stdout: If True, print to console. If False, return as string. sorting: If True, sort nodes at each level. If False, preserve

insertion order (key and reverse are ignored).

Returns:
str or None: If stdout=False, returns formatted string.

If stdout=True, prints to console and returns None.

Example:

Different display styles:

tree = Tree()
tree.create_node("Root", "root")
tree.create_node("Child A", "a", parent="root")
tree.create_node("Child B", "b", parent="root")

# Basic display
tree.show()

# Fancy style with IDs
tree.show(idhidden=False, line_type="ascii-em")

# Filtered display (only nodes starting with 'C')
tree.show(filter=lambda x: x.tag.startswith('C'))

# Sorted by tag, reversed
tree.show(key=lambda x: x.tag, reverse=True)

# Show custom data property
tree.show(data_property="name")  # If node.data.name exists

# Return as string instead of printing
tree_str = tree.show(stdout=False)

Output styles:

ascii:        Root
             |-- Child A
             |-- Child B

ascii-ex:     Root
             ├── Child A
             └── Child B

ascii-em:     Root
             ╠══ Child A
             ╚══ Child B
Note:

The tree display automatically handles complex hierarchies with proper indentation and connection lines. Very large trees may be truncated for performance reasons.

siblings(nid: str) list[Node][source]

Return the siblings of given @nid.

If @nid is root or there are no siblings, an empty list is returned.

size(level: int | None = None) int[source]

Get the number of nodes of the whole tree if @level is not given. Otherwise, the total number of nodes at specific level is returned.

@param level The level number in the tree. It must be between [0, tree.depth].

Otherwise, InvalidLevelNumber exception will be raised.

subtree(nid: str, identifier: str | None = None)[source]

Return a shallow COPY of subtree with nid being the new root. If nid is None, return an empty tree. If you are looking for a deepcopy, please create a new tree with this shallow copy, e.g.,

new_tree = Tree(t.subtree(t.root), deep=True)

This line creates a deep copy of the entire tree.

to_dict(nid=None, key=None, sort=True, reverse=False, with_data=False)[source]

Transform the whole tree into a dict.

to_graphviz(filename: str | None = None, shape: str = 'circle', graph: str = 'digraph', filter=None, key=None, reverse: bool = False, sorting: bool = True)[source]

Exports the tree in the dot format of the graphviz software

to_json(with_data: bool = False, sort: bool = True, reverse: bool = False)[source]

Export the tree structure as a JSON string.

Converts the entire tree into a JSON representation that can be easily saved, transmitted, or reconstructed. Perfect for data persistence, API responses, and cross-system integration.

Args:
with_data: If True, include node.data in the JSON output.

If False, only include tree structure and tags. Default False for smaller output size.

sort: If True, sort children at each level by node comparison.

If False, preserve insertion order.

reverse: If True, reverse the sorting order at each level.

Only applies when sort=True.

Returns:

str: JSON string representation of the tree.

Example:

Basic JSON export:

tree = Tree()
tree.create_node("Root", "root")
tree.create_node("Child A", "a", parent="root")
tree.create_node("Child B", "b", parent="root")

# Basic structure only
json_str = tree.to_json()
print(json_str)
# Output: {"Root": {"children": [{"Child A": {}}, {"Child B": {}}]}}

# Include node data
tree.create_node("Data Node", "data", parent="root",
               data={"type": "important", "value": 42})
json_with_data = tree.to_json(with_data=True)
print(json_with_data)
# Output includes: "data": {"type": "important", "value": 42}

# Sorted output
sorted_json = tree.to_json(sort=True, reverse=True)

JSON Structure:

The output follows this hierarchical format:

{
    "root_tag": {
        "children": [
            {
                "child1_tag": {
                    "children": [...],
                    "data": {...}  // if with_data=True
                }
            },
            {
                "child2_tag": {}  // leaf node
            }
        ],
        "data": {...}  // if with_data=True
    }
}

Usage with APIs:

# Save to file
with open('tree.json', 'w') as f:
    f.write(tree.to_json(with_data=True))

# Send via HTTP
import requests
response = requests.post('/api/trees',
                       json={'tree': tree.to_json()})

# Pretty print
import json
formatted = json.dumps(json.loads(tree.to_json()), indent=2)
print(formatted)
Note:

The JSON format preserves the complete tree structure and can be used to reconstruct equivalent trees. However, complex data objects in node.data must be JSON-serializable (no functions, custom classes without __dict__, etc.).

update_node(nid: str, **attrs) None[source]

Update node’s attributes.

Parameters:
  • nid – the identifier of modified node

  • attrs – attribute pairs recognized by Node object

Returns:

None

treelib.plugins module

treelib.exceptions module

exception treelib.exceptions.DuplicatedNodeIdError[source]

Bases: Exception

Exception throwed if an identifier already exists in a tree.

exception treelib.exceptions.InvalidLevelNumber[source]

Bases: Exception

exception treelib.exceptions.LinkPastRootNodeError[source]

Bases: Exception

Exception throwed in Tree.link_past_node() if one attempts to “link past” the root node of a tree.

exception treelib.exceptions.LoopError[source]

Bases: Exception

Exception thrown if trying to move node B to node A’s position while A is B’s ancestor.

exception treelib.exceptions.MultipleRootError[source]

Bases: Exception

Exception throwed if more than one root exists in a tree.

exception treelib.exceptions.NodeIDAbsentError[source]

Bases: NodePropertyError

Exception throwed if a node’s identifier is unknown

exception treelib.exceptions.NodePropertyAbsentError[source]

Bases: NodePropertyError

Exception throwed if a node’s data property is not specified

exception treelib.exceptions.NodePropertyError[source]

Bases: Exception

Basic Node attribute error