Online Treelib Compiler

Run treelib code in your browser. Build and print trees, walk branches, move or remove subtrees and export to a dict or JSON.

Python
from treelib import Node, Tree
from treelib.exceptions import DuplicatedNodeIdError
from collections.abc import Sized as root

tree = Tree()

def add_to_tree(node, parent=None):
    tree.create_node(node.__name__, id(node), parent=parent)
    subclasses = node.__subclasses__()
    for sc in subclasses:
        try:
            add_to_tree(sc, parent=id(node))
        except DuplicatedNodeIdError:
            pass

add_to_tree(root)
tree.show()
Sized
├── Collection
│   ├── Mapping
│   │   ├── LazyDict
│   │   ├── MutableMapping
│   │   │   ├── ChainMap
│   │   │   ├── SignalDict
│   │   │   ├── UserDict
│   │   │   ├── WeakKeyDictionary
│   │   │   ├── WeakValueDictionary
│   │   │   └── _Environ
│   │   └── _SelectorMapping
│   ├── Sequence
│   │   ├── ByteString
│   │   ├── MutableSequence
│   │   │   └── UserList
│   │   ├── UserString
│   │   └── _PathParents
│   ├── Set
│   │   ├── ItemsView
│   │   │   └── _OrderedDictItemsView
│   │   ├── KeysView
│   │   │   └── _OrderedDictKeysView
│   │   └── MutableSet
│   └── ValuesView
│       └── _OrderedDictValuesView
└── MappingView

treelib stores data as a tree, where every node except the root has exactly one parent. Each node has a tag, which is the label that gets printed, a unique identifier and an optional data value. The library prints trees as indented text, walks them depth first or breadth first, and moves or removes whole branches. It suits data such as org charts, category hierarchies and folder listings. For data where a node can have several parents, a graph library like NetworkX fits better. This page is an online treelib compiler: the code runs in your browser, so you can try it without installing anything. Run the example first, then paste any snippet below into a new cell to try it.

What the example does

The example draws part of Python's own class hierarchy, starting at collections.abc.Sized. add_to_tree() adds a class with tree.create_node(), using the class name as the tag and id(node) as the identifier, then calls itself for every class that __subclasses__() returns. The dictionary views KeysView, ItemsView and ValuesView inherit from MappingView and from Set or Collection, so the code reaches each of them twice. The second create_node() raises DuplicatedNodeIdError, which the code catches. That is why MappingView shows no children. tree.show() prints the result. Built-in types such as list are missing because they are registered with the abstract classes rather than inheriting from them. The other entries depend on which modules are loaded.

Build a tree from file paths

Using the full path as the identifier keeps two files with the same name apart, and contains() checks whether a folder is already there:

from treelib import Tree

paths = ["src/app.py", "src/utils/io.py", "src/utils/text.py",
         "README.md", "tests/test_app.py"]

tree = Tree()
tree.create_node("project", "project")
for path in paths:
    parent = "project"
    for part in path.split("/"):
        node_id = f"{parent}/{part}"
        if not tree.contains(node_id):
            tree.create_node(part, node_id, parent=parent)
        parent = node_id

tree.show()

Walk the tree

expand_tree() yields node identifiers, depth first by default or breadth first with mode=Tree.WIDTH. Other methods find a node's place in the tree:

from treelib import Tree

tree = Tree()
tree.create_node("Company", "co")
tree.create_node("Engineering", "eng", parent="co")
tree.create_node("Sales", "sales", parent="co")
tree.create_node("Platform", "platform", parent="eng")
tree.create_node("Mobile", "mobile", parent="eng")

print(list(tree.expand_tree()))                 # depth first
print(list(tree.expand_tree(mode=Tree.WIDTH)))  # breadth first
print([node.tag for node in tree.children("eng")])
print(tree.parent("mobile").tag, tree.level("mobile"), tree.depth())
print([node.tag for node in tree.leaves()])
print(tree.paths_to_leaves())

level() counts the steps from the root to a node, and depth() is the level of the deepest node.

Move, copy and remove branches

from treelib import Tree

tree = Tree()
tree.create_node("Company", "co")
tree.create_node("Engineering", "eng", parent="co")
tree.create_node("Sales", "sales", parent="co")
tree.create_node("Platform", "platform", parent="eng")
tree.create_node("Mobile", "mobile", parent="eng")

tree.move_node("mobile", "sales")    # Mobile moves under Sales
engineering = tree.subtree("eng")    # a new tree rooted at Engineering
removed = tree.remove_node("sales")  # removes Sales and everything below it
print("Removed", removed, "nodes")
tree.show()
engineering.show()

move_node() raises LoopError if you try to move a node under one of its own descendants.

Store data and export

from treelib import Tree

tree = Tree()
tree.create_node("Company", "co", data={"headcount": 120})
tree.create_node("Engineering", "eng", parent="co", data={"headcount": 70})
tree.create_node("Sales", "sales", parent="co", data={"headcount": 50})

print(tree["eng"].data["headcount"])
print(tree.to_dict())
print(tree.to_json(with_data=True))
print(tree.show(stdout=False))

to_dict() and to_json() key each node by its tag, not its identifier. show() prints the tree and returns None. With stdout=False it returns the text instead, ready to save to a file.

Good to know

  • show() and expand_tree() sort siblings by tag. Pass sorting=False to keep the order you added them in.
  • A tree has one root. A second node without a parent raises MultipleRootError, and a reused identifier raises DuplicatedNodeIdError. If you leave the identifier out, treelib generates a UUID string.
  • subtree() shares node objects with the original tree, so renaming a tag in one renames it in both. Tree(tree.subtree("eng"), deep=True) makes an independent copy.