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()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.
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.
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()
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.
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.
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.
show() and expand_tree() sort siblings by tag. Pass sorting=False
to keep the order you added them in.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.