Calmcode - rich: trees

Rendering a Tree in the Terminal with Rich.

1 2 3 4 5 6 7 8 9 10 11 12

Rich Python Trees

You can go to my Github profile to see an example use-case for rich. Rich can also render nested trees, which can give a very satisfying result.

Here's the implementation on my Github, it's an re-implementation from the creators profile.

from rich.console import Console
from rich.tree import Tree

console = Console(record=True, width=100)

tree = Tree("🙂 [link=https://koaning.io]Vincent D. Warmerdam", guide_style="bold bright_black")

python_tree = tree.add("📦 Open Source Packages", guide_style="bright_black")
python_tree.add("[bold link=https://scikit-lego.netlify.app/]scikit-lego[/] - [bright_black]lego bricks for sklearn")
python_tree.add("[bold link=https://koaning.github.io/human-learn/]human-learn[/] - [bright_black]rule-based components for sklearn")

online_tree = tree.add("⭐ Online Projects", guide_style="bright_black")
online_tree.add("[bold link=https://koaning.io]koaning.io[/]   - [bright_black]personal blog")
online_tree.add("[bold link=https://calmcode.io]calmcode.io[/]  - [bright_black]dev education service")

talk_tree = tree.add("🎙️ Popular Talks", guide_style="bright_black")
talk_tree.add("[bold link=https://youtu.be/qcrR-Hd0LhI?t=542]Optimal Benchmarks and Other Failures[/]")
talk_tree.add("[bold link=https://www.youtube.com/watch?v=nJAmN6gWdK8]Playing by the Rules-Based-Systems[/]")

employer_tree = tree.add("👨‍💻 Employer", guide_style="bright_black")
employer_tree.add("[bold link=https://rasa.com]Rasa[/] - [bright_black]conversational software")

console.print(tree)
console.print("")
console.print("[green]Follow me on twitter [bold link=https://twitter.com/fishnets88]@fishnets88[/]")

CONSOLE_HTML_FORMAT = """\
<pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">{code}</pre>
"""

console.save_html("README.md", inline_styles=True, code_format=CONSOLE_HTML_FORMAT)

Filestructures

Another great use-case for these trees is to use them to visualise the file structure. I have a script in my snippets library for just this use-case. Here's an example command.

python tree.py dirname

Here's the implementation of tree.py.

"""
Simple tree-render from the command line.
Originally from: https://github.com/willmcgugan/rich/blob/master/examples/tree.py
"""

import os
import pathlib
import sys

from rich import print
from rich.filesize import decimal
from rich.markup import escape
from rich.text import Text
from rich.tree import Tree


def walk_directory(directory: pathlib.Path, tree: Tree) -> None:
    """Recursively build a Tree with directory contents."""
    # Sort dirs first then by filename
    paths = sorted(
        pathlib.Path(directory).iterdir(),
        key=lambda path: (path.is_file(), path.name.lower()),
    )
    for path in paths:
        # Remove hidden files
        if path.name.startswith("."):
            continue
        if path.parts[-1] == "venv":
            continue
        if path.is_dir():
            style = "dim" if path.name.startswith("__") else ""
            branch = tree.add(
                f"[bold magenta]:open_file_folder: [link file://{path}]{escape(path.name)}",
                style=style,
                guide_style=style,
            )
            walk_directory(path, branch)
        else:
            text_filename = Text(path.name, "green")
            text_filename.highlight_regex(r"\..*$", "bold red")
            text_filename.stylize(f"link file://{path}")
            file_size = path.stat().st_size
            text_filename.append(f" ({decimal(file_size)})", "blue")
            icon = "🐍 " if path.suffix == ".py" else "📄 "
            tree.add(Text(icon) + text_filename)


try:
    directory = os.path.abspath(sys.argv[1])
except IndexError:
    print("[b]Usage:[/] python tree.py <DIRECTORY>")
else:
    tree = Tree(
        f":open_file_folder: [link file://{directory}]{directory}",
        guide_style="bold bright_blue",
    )
    walk_directory(pathlib.Path(directory), tree)
    print(tree)

Here's what the result might look like.

📂 /Users/vincent/Development/memo/build
┣━━ 📂 bdist.macosx-10.9-x86_64
┗━━ 📂 lib
    ┣━━ 📂 memo
    ┃   ┣━━ 🐍 __init__.py (422 bytes)
    ┃   ┣━━ 🐍 _base.py (2.7 kB)
    ┃   ┣━━ 🐍 _error.py (795 bytes)
    ┃   ┣━━ 🐍 _grid.py (1.3 kB)
    ┃   ┣━━ 🐍 _http.py (868 bytes)
    ┃   ┣━━ 🐍 _runner.py (5.1 kB)
    ┃   ┗━━ 🐍 _util.py (1.3 kB)
    ┗━━ 📂 tests
        ┣━━ 🐍 __init__.py (0 bytes)
        ┣━━ 🐍 conftest.py (0 bytes)
        ┣━━ 🐍 test_docs.py (690 bytes)
        ┣━━ 🐍 test_grid.py (428 bytes)
        ┣━━ 🐍 test_memfile.py (1.6 kB)
        ┣━━ 🐍 test_memlist.py (1.6 kB)
        ┗━━ 🐍 test_runner.py (2.5 kB)

Note the nice snake emoji's that render when it finds a python file!