
Rich transforme votre terminal en interface graphique. Couleurs, tableaux, barres de progression, arbres, logs enrichis, le tout dans un simple terminal. Ce guide vous fait construire pas à pas un vrai outil : un script de health check d'infrastructure qui scanne des services, affiche les résultats dans un tableau coloré, avec une barre de progression et des logs structurés. À chaque étape, vous découvrez une fonctionnalité de Rich parce que le script en a besoin.
Prérequis : savoir écrire des fonctions Python et utiliser print() (voir le
guide sur les fonctions).
Rich est aussi le moteur de rendu de Textual, le framework TUI. Tout ce que vous apprenez ici sur le markup et les styles s'applique directement dans Textual (voir le guide Textual).
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Remplacer
print()par uneConsoleRich pour des sorties nettes. - Colorer le texte avec le balisage (markup) Rich.
- Structurer des résultats en tables et en arbres.
- Ajouter des barres de progression aux scripts longs.
- Enrichir les logs avec
RichHandleret les tracebacks.
Le projet fil rouge : un health check d'infrastructure
Section intitulée « Le projet fil rouge : un health check d'infrastructure »Imaginez que vous gérez plusieurs services (un serveur web, une API, une base de données). Vous voulez un script qui :
- Scanne chaque service pour vérifier s'il répond
- Affiche les résultats de façon lisible dans le terminal
- Montre la progression du scan en temps réel
- Logue les événements importants avec horodatage
On va commencer avec un print() basique, puis améliorer le script section par
section avec Rich. À la fin, vous aurez un outil complet et réutilisable.
Voici le squelette de départ, un simple script avec print() :
"""health_check.py - Version de départ (sans Rich)."""
import randomimport time
# Nos services à surveillerservices = [ {"nom": "web-frontend", "ip": "10.0.1.10", "port": 80}, {"nom": "api-backend", "ip": "10.0.2.10", "port": 8080}, {"nom": "db-primary", "ip": "10.0.3.10", "port": 5432}, {"nom": "cache-redis", "ip": "10.0.4.10", "port": 6379}, {"nom": "monitoring", "ip": "10.0.5.10", "port": 9090},]
def scanner_service(service): """Simule un ping vers un service et renvoie la latence.""" time.sleep(0.3) # Simule le temps réseau latence = random.randint(1, 50) ok = latence < 100 return {"latence": latence, "ok": ok}
# Scanner et afficherprint("=== Health Check ===")for svc in services: resultat = scanner_service(svc) statut = "OK" if resultat["ok"] else "DOWN" print(f" {svc['nom']:20s} {svc['ip']:15s} {resultat['latence']:>4}ms {statut}")print("=== Terminé ===")Ça fonctionne, mais la sortie est monotone : tout est en blanc sur fond noir, pas de structure visuelle, impossible de distinguer les succès des échecs d'un coup d'œil. On va corriger ça.
Installation de Rich
Section intitulée « Installation de Rich »Rich est une bibliothèque Python pure, sans extension compilée : l'installation ne demande ni compilateur ni en-têtes système, et fonctionne à l'identique sur Linux, macOS et Windows. Épinglez la version dès maintenant, le rendu de certains composants évolue entre versions majeures et un script figé garde alors la même sortie.
pip install rich==15.0.0Vérifiez que l'installation fonctionne en lançant la démo intégrée :
python -m richSi vous voyez des couleurs, des tableaux et des emojis dans le terminal, c'est bon.
Étape 1 : remplacer print() par Console
Section intitulée « Étape 1 : remplacer print() par Console »Le premier pas avec Rich, c'est de remplacer print() par Console.print().
L'objet Console est le point d'entrée de toute utilisation de Rich :
from rich.console import Console
console = Console()
# Au lieu de print(), on utilise console.print()console.print("=== Health Check ===")Ça donne le même résultat, mais Console sait faire beaucoup plus. Par exemple,
il détecte et colore automatiquement les adresses IP, les nombres, les URLs
et les booléens :
console.print("Serveur : 10.0.1.10, port : 8080, latence : 12ms")# → les nombres et l'IP apparaissent en couleur automatiquementConsole dispose aussi de méthodes utiles qu'on va exploiter dans notre script
:
# rule() : crée un séparateur visuel - idéal pour structurer la sortieconsole.rule("Health Check")
# log() : affiche un message avec l'heure et le fichier sourceconsole.log("Scan démarré")# → [14:32:01] Scan démarré health_check.py:8
# print_json() : affiche du JSON formaté et coloréconsole.print_json('{"nom": "web-1", "cpu": 4, "ram_gb": 16}')Mettons à jour notre script pour utiliser Console :
"""health_check.py - Étape 1 : Console."""
import randomimport timefrom rich.console import Console
console = Console()
services = [ {"nom": "web-frontend", "ip": "10.0.1.10", "port": 80}, {"nom": "api-backend", "ip": "10.0.2.10", "port": 8080}, {"nom": "db-primary", "ip": "10.0.3.10", "port": 5432}, {"nom": "cache-redis", "ip": "10.0.4.10", "port": 6379}, {"nom": "monitoring", "ip": "10.0.5.10", "port": 9090},]
def scanner_service(service): time.sleep(0.3) latence = random.randint(1, 50) return {"latence": latence, "ok": latence < 100}
console.rule("Health Check")for svc in services: resultat = scanner_service(svc) statut = "OK" if resultat["ok"] else "DOWN" console.print(f" {svc['nom']:20s} {svc['ip']:15s} {resultat['latence']:>4}ms {statut}")console.rule("Terminé")C'est déjà plus lisible grâce aux séparateurs rule(), et les IP et nombres
sont colorés. Mais les statuts OK/DOWN sont toujours en blanc. On veut du
vert pour OK et du rouge pour DOWN.
Étape 2 : colorer avec le markup
Section intitulée « Étape 2 : colorer avec le markup »Rich utilise une syntaxe de markup inspirée du BBCode, des balises entre
crochets [], pour appliquer des couleurs et des styles au texte. La syntaxe
est simple : [style]texte[/style].
Voici les styles de base :
from rich.console import Consoleconsole = Console()
# Styles de texteconsole.print("[bold]Gras[/bold]")console.print("[italic]Italique[/italic]")console.print("[underline]Souligné[/underline]")console.print("[dim]Atténué[/dim]")
# Couleursconsole.print("[red]Rouge[/red]")console.print("[green]Vert[/green]")console.print("[yellow]Jaune[/yellow]")console.print("[blue on white]Bleu sur fond blanc[/blue on white]")
# Combinaisons - les styles se cumulentconsole.print("[bold red]Gras et rouge[/bold red]")console.print("[bold green on black]Gras vert sur fond noir[/bold green on black]")Le raccourci [/] ferme tous les styles ouverts d'un coup :
console.print("[bold red underline]Style complexe[/]") # tout ferméRich accepte aussi les couleurs hexadécimales et RGB :
console.print("[#ff8800]Orange hexadécimal[/#ff8800]")console.print("[rgb(100,200,50)]Vert RGB[/rgb(100,200,50)]")Et les emojis via leur code :
console.print(":white_check_mark: Service opérationnel")console.print(":warning: Attention : espace disque faible")console.print(":cross_mark: Service inaccessible")Tableau des styles disponibles
Section intitulée « Tableau des styles disponibles »Ces styles se combinent librement dans une même balise, séparés par des espaces : [bold red on white] cumule les trois. Le rendu réel dépend cependant du terminal, tous n'implémentent pas strike ni dim ; en cas de doute, python -m rich affiche l'échantillon complet dans votre terminal. La ligne link est la plus méconnue : elle produit un lien cliquable dans les terminaux compatibles, et se dégrade en texte simple ailleurs.
| Style | Syntaxe | Rendu |
|---|---|---|
| Gras | [bold]...[/bold] | Texte en gras |
| Italique | [italic]...[/italic] | Texte en italique |
| Souligné | [underline]...[/underline] | Texte souligné |
| Barré | [strike]...[/strike] | Texte barré |
| Atténué | [dim]...[/dim] | Texte grisé |
| Inversé | [reverse]...[/reverse] | Couleurs inversées |
| Couleur texte | [red]...[/red] | Texte en rouge |
| Couleur fond | [on blue]...[/on blue] | Fond bleu |
| Lien | [link=URL]...[/link] | Lien cliquable |
L'objet Style (pour le code)
Section intitulée « L'objet Style (pour le code) »Quand vous utilisez les mêmes couleurs à plusieurs endroits, plutôt que de répéter
le markup, définissez des objets Style réutilisables :
from rich.style import Style
STYLE_OK = Style(color="green", bold=True)STYLE_DOWN = Style(color="red", bold=True)STYLE_WARN = Style(color="yellow", bold=True)
console.print("Service opérationnel", style=STYLE_OK)console.print("Service inaccessible", style=STYLE_DOWN)Appliquons le markup à notre script de health check :
"""health_check.py - Étape 2 : markup couleur."""
import randomimport timefrom rich.console import Console
console = Console()
services = [ {"nom": "web-frontend", "ip": "10.0.1.10", "port": 80}, {"nom": "api-backend", "ip": "10.0.2.10", "port": 8080}, {"nom": "db-primary", "ip": "10.0.3.10", "port": 5432}, {"nom": "cache-redis", "ip": "10.0.4.10", "port": 6379}, {"nom": "monitoring", "ip": "10.0.5.10", "port": 9090},]
def scanner_service(service): time.sleep(0.3) latence = random.randint(1, 50) return {"latence": latence, "ok": latence < 100}
console.rule("[bold blue]Health Check")for svc in services: resultat = scanner_service(svc) if resultat["ok"]: statut = "[bold green]OK[/bold green]" style_latence = "green" else: statut = "[bold red]DOWN[/bold red]" style_latence = "red" latence = resultat["latence"] console.print( f" [cyan]{svc['nom']:20s}[/cyan] " f"[dim]{svc['ip']:15s}[/dim] " f"[{style_latence}]{latence:>4}ms[/{style_latence}] " f"{statut}" )console.rule("[bold blue]Terminé")Maintenant, les noms de services sont en cyan, les IP en gris, la latence est colorée, et le statut est en vert (OK) ou rouge (DOWN). En un coup d'œil, vous identifiez les problèmes. Mais la sortie ressemble encore à une liste de lignes. On peut faire mieux avec une table.
Étape 3 : structurer les résultats en table
Section intitulée « Étape 3 : structurer les résultats en table »Rich peut afficher des tables formatées directement dans le terminal, avec des colonnes alignées, des bordures et des styles par colonne. On crée une table en trois étapes : instancier, ajouter les colonnes, ajouter les lignes.
from rich.table import Table
# 1. Créer la tabletable = Table(title="État des services")
# 2. Ajouter les colonnestable.add_column("Service", style="cyan")table.add_column("Statut", justify="right")
# 3. Ajouter les lignestable.add_row("web-frontend", "OK")table.add_row("db-primary", "DOWN")
# 4. Afficherconsole.print(table)Options des colonnes
Section intitulée « Options des colonnes »Les options se passent à add_column(), colonne par colonne, et pilotent surtout la façon dont Rich répartit la largeur disponible. Par défaut la table s'adapte à la largeur du terminal et coupe le contenu trop long. Les deux options qui changent vraiment le rendu sont no_wrap, qui interdit le retour à la ligne au prix d'une troncature, et justify, à passer en right pour toute colonne numérique afin que les chiffres s'alignent sur les unités.
| Option | Description | Défaut |
|---|---|---|
style | Couleur/style du contenu | Aucun |
justify | Alignement (left, center, right) | left |
no_wrap | Empêcher le retour à la ligne | False |
width | Largeur fixe en caractères | Auto |
min_width / max_width | Limites de largeur | Aucun |
header_style | Style de l'en-tête | Aucun |
Styles de bordure
Section intitulée « Styles de bordure »Le style de bordure se choisit à la construction de la table, via une constante du module box. Ce n'est pas qu'une question d'esthétique : les traits épais et doubles utilisent des caractères Unicode que certains terminaux ou polices rendent mal. Pour une sortie destinée à être copiée dans un ticket ou un fichier texte, box=None reste le choix le plus sûr.
from rich import box
table = Table(box=box.ROUNDED) # Coins arrondis (le plus courant)table = Table(box=box.SIMPLE) # Lignes simplestable = Table(box=box.HEAVY) # Traits épaistable = Table(box=box.DOUBLE) # Double lignetable = Table(box=None) # Sans bordureIntégrons la table à notre script :
"""health_check.py - Étape 3 : résultats en table."""
import randomimport timefrom rich.console import Consolefrom rich.table import Tablefrom rich import box
console = Console()
services = [ {"nom": "web-frontend", "ip": "10.0.1.10", "port": 80}, {"nom": "api-backend", "ip": "10.0.2.10", "port": 8080}, {"nom": "db-primary", "ip": "10.0.3.10", "port": 5432}, {"nom": "cache-redis", "ip": "10.0.4.10", "port": 6379}, {"nom": "monitoring", "ip": "10.0.5.10", "port": 9090},]
def scanner_service(service): time.sleep(0.3) latence = random.randint(1, 50) return {"latence": latence, "ok": latence < 100}
# Scanner tous les servicesresultats = []for svc in services: resultat = scanner_service(svc) resultats.append({**svc, **resultat})
# Construire la tabletable = Table(title="Health Check", box=box.ROUNDED, show_lines=True)table.add_column("Service", style="cyan bold", no_wrap=True)table.add_column("IP", style="dim")table.add_column("Port", justify="right")table.add_column("Latence", justify="right")table.add_column("Statut", justify="center")
for r in resultats: style_latence = "green" if r["latence"] < 20 else "yellow" if r["latence"] < 50 else "red" statut = "[bold green]OK[/]" if r["ok"] else "[bold red]DOWN[/]" table.add_row( r["nom"], r["ip"], str(r["port"]), f"[{style_latence}]{r['latence']}ms[/]", statut, )
console.print(table)Résultat dans le terminal :
Health Check╭─────────────────┬────────────┬──────┬─────────┬────────╮│ Service │ IP │ Port │ Latence │ Statut │├─────────────────┼────────────┼──────┼─────────┼────────┤│ web-frontend │ 10.0.1.10 │ 80 │ 12ms │ OK │├─────────────────┼────────────┼──────┼─────────┼────────┤│ api-backend │ 10.0.2.10 │ 8080 │ 8ms │ OK │├─────────────────┼────────────┼──────┼─────────┼────────┤│ db-primary │ 10.0.3.10 │ 5432 │ 3ms │ OK │├─────────────────┼────────────┼──────┼─────────┼────────┤│ cache-redis │ 10.0.4.10 │ 6379 │ 42ms │ OK │├─────────────────┼────────────┼──────┼─────────┼────────┤│ monitoring │ 10.0.5.10 │ 9090 │ 28ms │ OK │╰─────────────────┴────────────┴──────┴─────────┴────────╯Les données sont structurées, alignées, colorées. Mais quand le scan prend du temps (beaucoup de services), l'utilisateur ne voit rien pendant l'attente. Il faut une barre de progression.
Étape 4 : ajouter une barre de progression
Section intitulée « Étape 4 : ajouter une barre de progression »Rich propose deux façons d'afficher une barre de progression :
La version simple : track()
Section intitulée « La version simple : track() »track() est la solution en une ligne. Vous l'utilisez à la place d'un
itérable dans une boucle for :
from rich.progress import track
for item in track(range(100), description="Traitement..."): time.sleep(0.02) # votre traitement iciTraitement... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 67% 0:00:01La version avancée : Progress()
Section intitulée « La version avancée : Progress() »Progress() permet de gérer plusieurs tâches et de personnaliser
l'affichage :
from rich.progress import Progress
with Progress() as progress: tache1 = progress.add_task("[cyan]Téléchargement...", total=100) tache2 = progress.add_task("[green]Installation...", total=100)
while not progress.finished: progress.update(tache1, advance=3) progress.update(tache2, advance=1) time.sleep(0.02)Téléchargement... ━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00Installation... ━━━━━━━━━━━━━━━━ 45% 0:00:02Intégrons track() à notre script pour montrer la progression du scan :
"""health_check.py - Étape 4 : barre de progression."""
import randomimport timefrom rich.console import Consolefrom rich.table import Tablefrom rich.progress import trackfrom rich import box
console = Console()
services = [ {"nom": "web-frontend", "ip": "10.0.1.10", "port": 80}, {"nom": "api-backend", "ip": "10.0.2.10", "port": 8080}, {"nom": "db-primary", "ip": "10.0.3.10", "port": 5432}, {"nom": "cache-redis", "ip": "10.0.4.10", "port": 6379}, {"nom": "monitoring", "ip": "10.0.5.10", "port": 9090},]
def scanner_service(service): time.sleep(0.3) latence = random.randint(1, 50) return {"latence": latence, "ok": latence < 100}
# Scanner avec barre de progressionresultats = []for svc in track(services, description="[cyan]Scan des services..."): resultat = scanner_service(svc) resultats.append({**svc, **resultat})
console.print()
# Table des résultatstable = Table(title="Health Check", box=box.ROUNDED, show_lines=True)table.add_column("Service", style="cyan bold", no_wrap=True)table.add_column("IP", style="dim")table.add_column("Port", justify="right")table.add_column("Latence", justify="right")table.add_column("Statut", justify="center")
for r in resultats: style_latence = "green" if r["latence"] < 20 else "yellow" if r["latence"] < 50 else "red" statut = "[bold green]OK[/]" if r["ok"] else "[bold red]DOWN[/]" table.add_row( r["nom"], r["ip"], str(r["port"]), f"[{style_latence}]{r['latence']}ms[/]", statut, )
console.print(table)Maintenant, l'utilisateur voit une barre progresser pendant le scan, puis le tableau de résultats. Le script devient réactif. Prochaine amélioration : on veut montrer l'architecture des services avant de les scanner.
Étape 5 : afficher une arborescence avec Tree
Section intitulée « Étape 5 : afficher une arborescence avec Tree »Tree affiche des données hiérarchiques, comme l'architecture d'un cluster,
l'arborescence de fichiers, ou la structure d'une organisation :
from rich.tree import Tree
# Créer l'arbre racinearbre = Tree(":cloud: Mon infrastructure")
# Ajouter des branchesfrontend = arbre.add("[green]Frontend")frontend.add("[cyan]web-1 (10.0.1.10)")frontend.add("[cyan]web-2 (10.0.1.11)")
backend = arbre.add("[yellow]Backend")backend.add("[cyan]api-1 (10.0.2.10)")
console.print(arbre)☁ Mon infrastructure├── Frontend│ ├── web-1 (10.0.1.10)│ └── web-2 (10.0.1.11)└── Backend └── api-1 (10.0.2.10)Encadrer avec Panel
Section intitulée « Encadrer avec Panel »Panel encadre n'importe quel contenu Rich dans un cadre avec titre :
from rich.panel import Panel
console.print(Panel( "Contenu encadré", title="Mon titre", subtitle="Mon sous-titre", border_style="blue",))
# Panel ajusté au contenuconsole.print(Panel.fit("Texte court", title="Info"))On combine Tree et Panel pour afficher l'architecture avant le scan :
"""health_check.py - Étape 5 : architecture avec Tree et Panel."""
import randomimport timefrom rich.console import Consolefrom rich.table import Tablefrom rich.panel import Panelfrom rich.tree import Treefrom rich.progress import trackfrom rich import box
console = Console()
services = [ {"nom": "web-frontend", "ip": "10.0.1.10", "port": 80, "groupe": "Frontend"}, {"nom": "api-backend", "ip": "10.0.2.10", "port": 8080, "groupe": "Backend"}, {"nom": "db-primary", "ip": "10.0.3.10", "port": 5432, "groupe": "Base de données"}, {"nom": "cache-redis", "ip": "10.0.4.10", "port": 6379, "groupe": "Cache"}, {"nom": "monitoring", "ip": "10.0.5.10", "port": 9090, "groupe": "Observabilité"},]
def scanner_service(service): time.sleep(0.3) latence = random.randint(1, 50) return {"latence": latence, "ok": latence < 100}
# 1. Afficher l'architecturearbre = Tree(":cloud: Infrastructure de production")groupes = {}for svc in services: groupe = svc["groupe"] if groupe not in groupes: groupes[groupe] = arbre.add(f"[bold]{groupe}[/]") groupes[groupe].add(f"[cyan]{svc['nom']}[/] ([dim]{svc['ip']}:{svc['port']}[/])")
console.print(Panel(arbre, title="Architecture", border_style="blue"))console.print()
# 2. Scanner avec barre de progressionresultats = []for svc in track(services, description="[cyan]Scan des services..."): resultat = scanner_service(svc) resultats.append({**svc, **resultat})
console.print()
# 3. Table des résultatstable = Table(title="Résultats du Health Check", box=box.ROUNDED, show_lines=True)table.add_column("Service", style="cyan bold", no_wrap=True)table.add_column("IP", style="dim")table.add_column("Latence", justify="right")table.add_column("Statut", justify="center")
for r in resultats: style_latence = "green" if r["latence"] < 20 else "yellow" if r["latence"] < 50 else "red" statut = "[bold green]OK[/]" if r["ok"] else "[bold red]DOWN[/]" table.add_row(r["nom"], r["ip"], f"[{style_latence}]{r['latence']}ms[/]", statut)
console.print(table)console.print()
# 4. Résumé encadrétotal = len(resultats)ok_count = sum(1 for r in resultats if r["ok"])couleur = "green" if ok_count == total else "yellow" if ok_count > 0 else "red"console.print(Panel.fit( f"[bold]{ok_count}/{total}[/] services opérationnels\n" f"Latence moyenne : [cyan]{sum(r['latence'] for r in resultats) / total:.0f}ms[/]", title="Résumé", border_style=couleur,))Le script affiche maintenant quatre sections : l'architecture en arbre, la barre de progression du scan, le tableau de résultats, et un résumé encadré. C'est déjà un outil utilisable. Mais si quelque chose plante, on veut des logs lisibles et des tracebacks utiles.
Étape 6 : ajouter des logs enrichis
Section intitulée « Étape 6 : ajouter des logs enrichis »Python dispose d'un module logging intégré pour écrire des messages de log.
Par défaut, les logs sont en texte brut sans couleurs. RichHandler remplace le
handler par défaut pour afficher les logs avec horodatage, couleurs par
niveau, et le nom du fichier source :
import loggingfrom rich.logging import RichHandler
# Configuration en une lignelogging.basicConfig( level=logging.DEBUG, format="%(message)s", datefmt="[%X]", handlers=[RichHandler(rich_tracebacks=True)])
log = logging.getLogger("health_check")
log.debug("Détail technique pour le debug")log.info("Scan démarré")log.warning("Latence élevée sur cache-redis")log.error("Service db-primary inaccessible")log.critical("Aucun service ne répond !")[14:32:01] DEBUG Détail technique pour le debug health_check.py:12[14:32:01] INFO Scan démarré health_check.py:13[14:32:01] WARNING Latence élevée sur cache-redis health_check.py:14[14:32:01] ERROR Service db-primary inaccessible health_check.py:15[14:32:01] CRITICAL Aucun service ne répond ! health_check.py:16Chaque niveau a sa couleur (DEBUG gris, INFO bleu, WARNING jaune, ERROR rouge, CRITICAL rouge gras), et Rich affiche automatiquement le fichier et le numéro de ligne.
Options de RichHandler
Section intitulée « Options de RichHandler »Regardez d'abord la colonne des valeurs par défaut : rich_tracebacks et tracebacks_show_locals sont à False, alors que ce sont précisément les deux options qui rendent RichHandler utile en dépannage. Sans elles, une exception loguée par log.exception() s'affiche avec le traceback Python brut, et vous perdez le bénéfice de la bibliothèque au moment exact où vous en avez besoin.
| Option | Description | Défaut |
|---|---|---|
rich_tracebacks | Tracebacks enrichis et colorés | False |
show_time | Afficher l'heure | True |
show_level | Afficher le niveau (INFO, ERROR...) | True |
show_path | Afficher fichier:ligne | True |
markup | Interpréter le markup Rich dans les messages | False |
tracebacks_show_locals | Afficher les variables locales dans les tracebacks | False |
Étape 7 : des tracebacks lisibles
Section intitulée « Étape 7 : des tracebacks lisibles »Quand un script plante, le traceback Python standard est souvent difficile à lire. Rich le remplace par une version colorée avec le code source et les variables locales :
from rich.traceback import install
# Active les tracebacks Rich pour tout le scriptinstall(show_locals=True)Traceback (most recent call last): File "health_check.py", line 12, in <module> result = process_config(data) File "health_check.py", line 8, in process_config return config["database"]["host"]KeyError: 'database'╭──────── Traceback (most recent call last) ────────╮│ health_check.py:12 in <module> ││ ││ 10 │ data = {"app": "web", "port": 8080} ││ 11 │ ││ ❱ 12 │ result = process_config(data) ││ 13 │ ││ ╭─ locals ──╮ ││ │ data = {'app': 'web', 'port': 8080} ││ ╰───────────╯ ││ ││ health_check.py:8 in process_config ││ ❱ 8 │ return config["database"]["host"] ││ ╭─ locals ──╮ ││ │ config = {'app': 'web', 'port': 8080} ││ ╰───────────╯ │╰────────────────────────────────────────────────────╯KeyError: 'database'La version Rich montre le code source autour de l'erreur, les variables locales à chaque niveau, et une coloration syntaxique qui aide à repérer le problème immédiatement. Activez-le au début de vos scripts pour ne plus jamais revenir en arrière.
Étape 8 : Status et Live (affichage dynamique)
Section intitulée « Étape 8 : Status et Live (affichage dynamique) »Spinner avec console.status()
Section intitulée « Spinner avec console.status() »Pour indiquer qu'un travail est en cours sans barre de progression (quand vous ne
connaissez pas le nombre d'étapes), utilisez console.status() :
with console.status("[bold green]Connexion au cluster...") as status: time.sleep(2) status.update("[bold yellow]Vérification des certificats...") time.sleep(1) status.update("[bold cyan]Chargement de la config...") time.sleep(1)
console.print("[bold green]Connecté !")Un spinner animé tourne dans le terminal pendant chaque étape.
Mise à jour en temps réel avec Live
Section intitulée « Mise à jour en temps réel avec Live »Live permet de mettre à jour un affichage (table, panel...) sans scintillement
ni défilement. L'affichage se rafraîchit sur place :
from rich.live import Live
def generer_table(etape): """Génère une table de statut avec l'état actuel du scan.""" table = Table(title="Scan en cours") table.add_column("Service") table.add_column("Statut")
noms = ["web-frontend", "api-backend", "db-primary", "cache-redis", "monitoring"] for i, nom in enumerate(noms): if i < etape: table.add_row(nom, "[green]Scanné") elif i == etape: table.add_row(nom, "[yellow]En cours...") else: table.add_row(nom, "[dim]En attente") return table
with Live(generer_table(0), console=console, refresh_per_second=4) as live: for etape in range(5): time.sleep(0.5) live.update(generer_table(etape + 1))La table se met à jour en temps réel : chaque service passe de "En attente" à "En cours" puis "Scanné", le tout sans que le terminal défile.
Étape bonus : explorer un objet avec inspect()
Section intitulée « Étape bonus : explorer un objet avec inspect() »Pendant le développement, vous pouvez utiliser rich.inspect() pour explorer
n'importe quel objet Python, ses attributs, ses méthodes, sa documentation :
from rich import inspect
# Inspecter un dictionnaireinspect({"nom": "web-1", "ip": "10.0.1.10"})
# Inspecter avec les méthodes visiblesinspect(["a", "b", "c"], methods=True)
# Inspecter un module entierimport osinspect(os, methods=True)╭──────────────── <class 'dict'> ────────────────╮│ {'nom': 'web-1', 'ip': '10.0.1.10'} ││ ││ clear = def clear(...) ││ copy = def copy(...) ││ keys = def keys(...) ││ values = def values(...) ││ items = def items(...) ││ ... │╰─────────────────────────────────────────────────╯Script final complet
Section intitulée « Script final complet »Voici le script complet qui rassemble toutes les fonctionnalités vues dans ce guide. Vous pouvez le copier et l'adapter à votre propre infrastructure :
"""health_check.py - Script complet de health check avec Rich."""
import loggingimport randomimport time
from rich.console import Consolefrom rich.logging import RichHandlerfrom rich.panel import Panelfrom rich.progress import trackfrom rich.table import Tablefrom rich.traceback import installfrom rich.tree import Treefrom rich import box
# --- Configuration ---install(show_locals=True) # Tracebacks Rich pour tout le script
logging.basicConfig( level=logging.INFO, format="%(message)s", datefmt="[%X]", handlers=[RichHandler(rich_tracebacks=True)],)log = logging.getLogger("health_check")
console = Console()
# --- Données ---services = [ {"nom": "web-frontend", "ip": "10.0.1.10", "port": 80, "groupe": "Frontend"}, {"nom": "api-backend", "ip": "10.0.2.10", "port": 8080, "groupe": "Backend"}, {"nom": "db-primary", "ip": "10.0.3.10", "port": 5432, "groupe": "Base de données"}, {"nom": "cache-redis", "ip": "10.0.4.10", "port": 6379, "groupe": "Cache"}, {"nom": "monitoring", "ip": "10.0.5.10", "port": 9090, "groupe": "Observabilité"},]
def scanner_service(service): """Simule un health check réseau.""" time.sleep(0.3) latence = random.randint(1, 80) return {"latence": latence, "ok": latence < 100}
def afficher_architecture(services): """Affiche l'arborescence des services dans un panel.""" arbre = Tree(":cloud: Infrastructure de production") groupes = {} for svc in services: groupe = svc["groupe"] if groupe not in groupes: groupes[groupe] = arbre.add(f"[bold]{groupe}[/]") groupes[groupe].add( f"[cyan]{svc['nom']}[/] ([dim]{svc['ip']}:{svc['port']}[/])" ) console.print(Panel(arbre, title="Architecture", border_style="blue"))
def scanner_tous(services): """Scanne chaque service avec une barre de progression.""" resultats = [] for svc in track(services, description="[cyan]Scan des services..."): resultat = scanner_service(svc) resultats.append({**svc, **resultat}) if not resultat["ok"]: log.error(f"{svc['nom']} est DOWN (latence : {resultat['latence']}ms)") elif resultat["latence"] > 30: log.warning(f"{svc['nom']} - latence élevée : {resultat['latence']}ms") else: log.info(f"{svc['nom']} - OK ({resultat['latence']}ms)") return resultats
def afficher_resultats(resultats): """Affiche les résultats dans une table colorée.""" table = Table( title="Résultats du Health Check", box=box.ROUNDED, show_lines=True, ) table.add_column("Service", style="cyan bold", no_wrap=True) table.add_column("Groupe", style="dim") table.add_column("IP", style="dim") table.add_column("Latence", justify="right") table.add_column("Statut", justify="center")
for r in resultats: if r["latence"] < 20: style_latence = "green" elif r["latence"] < 50: style_latence = "yellow" else: style_latence = "red" statut = "[bold green]OK[/]" if r["ok"] else "[bold red]DOWN[/]" table.add_row( r["nom"], r["groupe"], f"{r['ip']}:{r['port']}", f"[{style_latence}]{r['latence']}ms[/]", statut, ) console.print(table)
def afficher_resume(resultats): """Affiche un résumé encadré.""" total = len(resultats) ok_count = sum(1 for r in resultats if r["ok"]) latence_moy = sum(r["latence"] for r in resultats) / total
if ok_count == total: couleur = "green" icone = ":white_check_mark:" elif ok_count > 0: couleur = "yellow" icone = ":warning:" else: couleur = "red" icone = ":cross_mark:"
console.print(Panel.fit( f"{icone} [bold]{ok_count}/{total}[/] services opérationnels\n" f"Latence moyenne : [cyan]{latence_moy:.0f}ms[/]", title="Résumé", border_style=couleur, ))
# --- Exécution ---console.rule("[bold blue]Health Check - Production")console.print()
afficher_architecture(services)console.print()
resultats = scanner_tous(services)console.print()
afficher_resultats(resultats)console.print()
afficher_resume(resultats)Lancez-le :
python health_check.pyLe terminal affiche successivement : l'arbre d'architecture, la barre de progression du scan, les logs horodatés par service, le tableau de résultats coloré, et le résumé encadré avec une icône verte/jaune/rouge.
Exporter la sortie
Section intitulée « Exporter la sortie »Vous avez peut-être envie d'envoyer ce rapport par email ou de l'archiver. Rich peut exporter la sortie en texte brut ou en HTML :
# Activer l'enregistrementconsole = Console(record=True)
# ... tout le script s'exécute ici ...
# Exporter en texte brut (sans codes couleur)with open("rapport.txt", "w") as f: f.write(console.export_text())
# Exporter en HTML (avec les couleurs préservées)with open("rapport.html", "w") as f: f.write(console.export_html())Pour écrire directement dans un fichier sans couleurs :
with open("rapport.txt", "w") as f: fichier_console = Console(file=f, force_terminal=False) fichier_console.print("Rapport sans couleurs")Bonnes pratiques
Section intitulée « Bonnes pratiques »Quand utiliser chaque fonctionnalité
Section intitulée « Quand utiliser chaque fonctionnalité »Ce tableau se lit par la colonne de gauche : partez du besoin réel de votre script, pas du composant qui vous plaît. La confusion la plus fréquente concerne les trois indicateurs d'attente. track() suppose que vous connaissez le nombre d'itérations, Progress() sert quand plusieurs traitements avancent en parallèle, et console.status() couvre le cas où la durée est inconnue. Choisir le mauvais des trois donne une barre bloquée à 0 % ou une progression qui saute d'un coup à 100 %.
| Besoin | Fonctionnalité Rich | Quand l'utiliser |
|---|---|---|
| Afficher un résultat | console.print() + markup | Toujours, remplace print() |
| Structurer des données | Table | Listes de serveurs, résultats, comparaisons |
| Montrer une hiérarchie | Tree + Panel | Architecture, arborescence fichiers, organisations |
| Suivre un traitement long | track() | Boucle dont vous connaissez le nombre d'itérations |
| Tâches parallèles | Progress() | Plusieurs progressions simultanées |
| Attente indéterminée | console.status() | Connexion, chargement, sans nombre d'étapes |
| Mise à jour en place | Live | Dashboard, monitoring en temps réel |
| Journaliser | RichHandler | Tout script qui utilise logging |
| Débugger un crash | install() de traceback | En début de script, toujours |
| Explorer un objet | inspect() | En debug uniquement |
Performance
Section intitulée « Performance »Rich reste largement assez rapide pour un outil d'administration, mais il fait beaucoup plus de travail que print() : analyse du markup, calcul des largeurs, génération des séquences ANSI. Les trois points ci-dessous couvrent les seuls cas où cela se remarque, tous liés à un volume inhabituel de sorties ou à de la mémoire retenue inutilement.
Console(record=True): active l'enregistrement uniquement si vous exportez, ça consomme de la mémoire.track(): préférez-le àProgress()pour les cas simples.- Markup dans les boucles serrées : pour des millions d'itérations,
print()natif reste plus rapide.
Compatibilité terminal
Section intitulée « Compatibilité terminal »Rich adapte sa sortie à la destination : il interroge le terminal pour connaître sa largeur et son niveau de couleur, et bascule en texte simple quand la sortie est redirigée. Ce comportement est souhaitable en production mais déroutant en CI, où les logs arrivent sans la moindre couleur alors que tout fonctionne en local. Les trois réglages ci-dessous couvrent ces situations.
- Rich détecte automatiquement si le terminal supporte les couleurs.
- En CI/CD : Rich désactive les couleurs si stdout n'est pas un TTY.
Force-les avec
Console(force_terminal=True). - Redirection vers un fichier : utilisez
Console(file=f)pour un export propre sans codes ANSI.
Dépannage
Section intitulée « Dépannage »Presque tous les problèmes rencontrés avec Rich viennent d'un décalage entre ce que la bibliothèque croit de son terminal et la réalité : largeur, support des couleurs, présence d'un TTY, police avec ou sans emoji. La deuxième cause, plus banale, est un print() natif resté quelque part dans le code, qui affiche le markup tel quel au lieu de l'interpréter.
| Symptôme | Cause probable | Solution |
|---|---|---|
| Pas de couleurs dans le terminal | Terminal ne supporte pas les couleurs | Vérifiez avec python -m rich |
| Couleurs absentes en CI/CD | stdout n'est pas un TTY | Console(force_terminal=True) |
| Texte tronqué dans les tables | Terminal trop étroit | Console(width=120) ou réduisez les colonnes |
[red]texte[/red] affiché tel quel | Vous utilisez print() au lieu de console.print() | Remplacez par Console().print() |
| Markup interprété dans les logs | markup=True sur RichHandler | Mettez markup=False si vous loguez des données brutes |
| Emoji non affiché | Terminal sans support emoji | Console(emoji=False) |
| Export HTML sans couleurs | Oubli de record=True | Console(record=True) avant les print() |
FAQ : Rich en Python
Section intitulée « FAQ : Rich en Python »- couleurs et styles via un balisage simple,
- tableaux, arbres, barres de progression,
- syntaxe colorée et tracebacks lisibles.
pip install rich
from rich.console import Console
console = Console()
console.print("[bold green]OK[/] service en ligne")
On remplace print par un objet Console pour un rendu bien plus clair, sans changer la logique du script.Table :from rich.console import Console
from rich.table import Table
table = Table(title="Services")
table.add_column("Nom")
table.add_column("État", justify="right")
table.add_row("nginx", "[green]actif[/]")
table.add_row("postgres", "[red]arrêté[/]")
Console().print(table)
Rich gère l'alignement, les bordures et la couleur automatiquement. Idéal pour présenter un état d'infrastructure ou un rapport.track :from rich.progress import track
import time
for serveur in track(serveurs, description="Vérification..."):
verifier(serveur)
time.sleep(0.2)
Rich affiche une barre animée avec le pourcentage et le temps restant. Pour suivre plusieurs tâches en parallèle ou personnaliser les colonnes, utilisez la classe Progress, plus complète.logging standard via RichHandler :import logging
from rich.logging import RichHandler
logging.basicConfig(
level=logging.INFO,
format="%(message)s",
handlers=[RichHandler()],
)
logging.info("Service démarré")
logging.error("Connexion refusée")
Les logs s'affichent colorés, horodatés et avec le niveau mis en valeur, sans toucher à vos appels logging.info ou logging.error existants.- Rich embellit des sorties non interactives : tableaux, couleurs, barres de progression affichés puis terminés. Parfait pour un rapport ou un script.
- Textual construit des applications terminal interactives : widgets, événements clavier, navigation entre écrans.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
À retenir
Section intitulée « À retenir »- Rich remplace
print()parConsole.print(), couleurs, markup, détection automatique d'IP/URL, et export. - Le markup
[bold red]texte[/]applique des styles combinables en une syntaxe simple. - Les tables structurent les résultats avec colonnes alignées, bordures et couleurs par colonne.
track()ajoute une barre de progression en une seule ligne de changement.TreeetPanelaffichent des hiérarchies et encadrent les résultats, pour un rendu professionnel.RichHandlerremplace le logging standard avec horodatage, couleurs par niveau et fichier source.install()derich.tracebackrend les erreurs lisibles avec code source et variables locales.- Rich est le moteur de Textual : le markup et les styles appris ici s'appliquent dans les TUI.
- Le fil rouge : chaque fonctionnalité Rich résout un vrai besoin du script, couleur pour distinguer OK/DOWN, table pour structurer, progression pour l'attente, logs pour tracer, tracebacks pour débugger.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Scanner un réseau avec nmap : remplacez le scan simulé du fil rouge par de vraies sondes réseau, puis affichez les résultats dans une table Rich.
- Fabric : automatiser en SSH : combinez Rich avec l'exécution de commandes à distance pour un tableau de bord d'administration coloré.