Aller au contenu
Développement medium

Créer une CLI Python avec Click

17 min de lecture

Créer des applications en ligne de commande (CLI) avec Python peut sembler intimidant au début, surtout quand on pense à toutes les options et arguments à gérer. Si vous avez déjà exploré les modules standard comme argparse ou optparse, vous avez sûrement remarqué que la gestion des erreurs, des messages d’aide, ou encore des formats d’arguments peut vite devenir un casse-tête.

Click, une librairie Python développée par l’équipe de Pallets (les créateurs de Flask), est conçue justement pour simplifier ce travail. Avec Click, vous pouvez créer des CLI robustes et élégantes en quelques lignes de code seulement. Elle repose sur un modèle simple basé sur les décorateurs, ce qui rend le code plus lisible et plus facile à maintenir. En quelques clics, sans mauvais jeu de mots, vous pouvez transformer un simple script Python en une CLI puissante et interactive.

Pourquoi choisir Click plutôt qu’un autre module ? La réponse est dans la philosophie de la librairie : faire en sorte que le développement soit un jeu d’enfant tout en restant suffisamment flexible pour des projets complexes. Au lieu de se perdre dans des configurations interminables, Click vous permet de vous concentrer sur les fonctionnalités et de produire un code propre.

En clair, si vous avez besoin de créer des commandes, des sous-commandes, des options ou même des validations d’arguments pour guider vos utilisateurs, Click est l’outil idéal.

Avant de plonger dans le code, il faut évidemment installer la librairie. Heureusement, Click s’installe très facilement avec pip, le gestionnaire de paquets Python.

Fenêtre de terminal
pip install click

Pour vérifier que l’installation s’est bien déroulée, lancez la commande suivante et assurez-vous qu’aucune erreur ne s’affiche :

Fenêtre de terminal
python -m click --version

Vous devriez voir s’afficher la version de Click installée. Si tout est en ordre, vous êtes prêt à commencer ! Une installation réussie, c’est déjà un pied dans la création d’une application en ligne de commande. Simple et efficace, non ?

Maintenant que Click est installé, il est temps de créer votre première commande en ligne de commande, ou CLI. Pas de panique, on va commencer par une commande basique pour bien comprendre le fonctionnement.

Voici un exemple de code pour créer une commande simple qui dit “Hello” à l’utilisateur :

import click
@click.command()
@click.option('--name', default='World', help='Nom de la personne à saluer')
def hello(name):
click.echo(f"Hello, {name}!")

Dans cet exemple, vous avez quelques éléments essentiels :

  • @click.command() : Ce décorateur indique que la fonction hello est une commande CLI.
  • @click.option() : Il permet de définir des options pour la commande. Ici, on crée une option --name, avec la valeur par défaut “World” et un message d’aide pour l’utilisateur.

Pour lancer cette commande, enregistrez le code dans un fichier, par exemple hello.py et exécutez la commande suivante dans votre terminal :

Fenêtre de terminal
python hello.py --name Alice

Vous devriez voir afficher :

Hello, Alice!

Et si vous exécutez simplement python hello.py sans option, le programme utilisera la valeur par défaut :

Hello, World!

Une fois à l’aise avec la création de commandes de base, passons aux arguments et options de Click. La différence est simple : un argument est un paramètre obligatoire de la commande, tandis qu’une option est facultative et peut avoir une valeur par défaut.

Un argument est souvent essentiel à la commande. Par exemple, disons que vous voulez créer une commande pour afficher un message de bienvenue à une personne en fonction de son prénom et nom, tous deux nécessaires.

import click
@click.command()
@click.argument('firstname')
@click.argument('lastname')
def greet(firstname, lastname):
click.echo(f"Bienvenue, {firstname} {lastname}!")

Ici, firstname et lastname sont des arguments de la commande. Ils doivent être fournis lorsque vous exécutez la commande :

Fenêtre de terminal
python greet.py Alice Dupont

Cela affichera :

Bienvenue, Alice Dupont!

Si vous oubliez de fournir l’un des arguments, Click affichera un message d’erreur clair pour guider l’utilisateur.

Les options, en revanche, sont souvent facultatives et permettent d’ajuster le comportement de la commande. Par exemple, ajoutons une option --age pour indiquer l’âge de la personne :

import click
@click.command()
@click.argument('firstname')
@click.argument('lastname')
@click.option('--age', default=18, help="Âge de la personne")
def greet(firstname, lastname, age):
click.echo(f"Bienvenue, {firstname} {lastname}! Vous avez {age} ans.")

Dans ce cas, age est une option facultative. Si elle n’est pas fournie, la valeur par défaut (ici 18) sera utilisée :

Fenêtre de terminal
python greet.py Alice Dupont --age 30

Sortie :

Bienvenue, Alice Dupont! Vous avez 30 ans.

Si vous omettez --age, la sortie sera :

Bienvenue, Alice Dupont! Vous avez 18 ans.

Pour les applications CLI comportant plusieurs commandes, Click permet d’organiser ces commandes en groupes. Un groupe rassemble des sous-commandes, rendant l’utilisation plus intuitive, surtout quand on manipule plusieurs actions liées.

Prenons l’exemple d’une application de gestion de services, avec des sous-commandes pour démarrer (start), arrêter (stop) et vérifier le statut (status) d’un service.

Voici comment structurer cela avec Click :

import click
@click.group()
def service():
"""Groupe de commandes pour gérer un service."""
pass
@service.command()
def start():
click.echo("Service démarré")
@service.command()
def stop():
click.echo("Service arrêté")
@service.command()
def status():
click.echo("Service en cours de fonctionnement")

Dans cet exemple :

  • @click.group() : Ce décorateur définit une commande de niveau supérieur, ici service, qui regroupe plusieurs sous-commandes.
  • @service.command() : Ce décorateur crée une sous-commande pour chaque action (start, stop, status) au sein du groupe.

Pour utiliser ces sous-commandes, il suffit de lancer la commande principale suivie de la sous-commande :

Fenêtre de terminal
python service.py start

Cela affiche :

Service démarré

Et pour vérifier le statut :

Fenêtre de terminal
python service.py status

Résultat :

Service en cours de fonctionnement

Click permet également d’ajouter des descriptions et des messages d’aide pour chaque sous-commande, afin de guider les utilisateurs et rendre les actions plus claires :

@service.command(help="Démarre le service")
def start():
click.echo("Service démarré")

Pour une CLI efficace, il est essentiel de guider les utilisateurs lorsqu’ils se trompent et de vérifier les entrées afin d’éviter des comportements inattendus. Click propose des outils de validation et de gestion des erreurs qui permettent de renforcer la robustesse de votre application.

Click offre la possibilité de restreindre les types d’arguments et d’options en les spécifiant directement dans les décorateurs. Par exemple, vous pouvez imposer qu’un argument soit un entier ou qu’une option soit un fichier.

import click
@click.command()
@click.argument('count', type=click.IntRange(1, 10))
def repeat(count):
click.echo(f"Répétez {count} fois")

Ici, count doit être un entier entre 1 et 10. Si l’utilisateur saisit une valeur en dehors de cette plage, Click génère automatiquement un message d’erreur.

Parfois, il est utile de limiter les options d’une commande à quelques choix spécifiques. Click permet cela avec click.Choice, qui propose à l’utilisateur une liste de choix valides.

@click.command()
@click.option('--niveau', type=click.Choice(['débutant', 'intermédiaire', 'expert'], case_sensitive=False))
def apprentissage(niveau):
click.echo(f"Niveau d'apprentissage sélectionné : {niveau}")

Si l’utilisateur entre une valeur autre que celles définies, Click affichera un message d’erreur pour lui indiquer les choix possibles.

Click facilite aussi la gestion des fichiers en garantissant que les fichiers fournis par l’utilisateur existent et sont accessibles. Par exemple :

@click.command()
@click.option('--fichier', type=click.File('r'), help="Chemin du fichier à lire")
def lire_fichier(fichier):
contenu = fichier.read()
click.echo(contenu)

Ici, si le fichier spécifié n’existe pas ou ne peut pas être ouvert, Click informe l’utilisateur sans avoir à coder de vérification complexe.

Parfois, les erreurs peuvent nécessiter des messages plus précis. Click permet de lever des exceptions personnalisées pour afficher des erreurs spécifiques :

import click
@click.command()
@click.argument('age', type=int)
def verifier_age(age):
if age < 18:
raise click.BadParameter("L'âge doit être d'au moins 18 ans.")
click.echo("Âge valide.")

Si l’utilisateur entre un âge en dessous de 18, le message d’erreur est personnalisé et donne des informations spécifiques.

Un bon retour visuel dans une CLI est essentiel pour garder l’utilisateur informé et rendre l’outil agréable à utiliser. Click propose plusieurs méthodes pour styliser et formater les sorties, vous permettant d’afficher des messages clairs, colorés et adaptés à chaque situation.

La méthode de base pour afficher des messages est click.echo(), qui simplifie l’affichage en s’occupant des encodages et en étant compatible avec différentes plateformes.

import click
@click.command()
def simple_echo():
click.echo("Voici un message simple.")

Cela fonctionne comme un print() classique, mais avec une meilleure prise en charge des environnements CLI.

Pour une présentation plus dynamique, click.style() permet d’ajouter des effets visuels aux messages, comme des couleurs, des styles de texte, ou des arrière-plans.

@click.command()
def styled_message():
click.echo(click.style("Succès !", fg="green", bold=True))
click.echo(click.style("Erreur...", fg="red", bg="yellow"))

Dans cet exemple :

  • fg (foreground) définit la couleur du texte.
  • bg (background) définit la couleur d’arrière-plan.
  • bold rend le texte en gras.

Si vous voulez styliser et afficher un message en une seule étape, utilisez click.secho(), qui combine click.echo() et click.style().

@click.command()
def message_styled():
click.secho("Action réussie !", fg="green")
click.secho("Attention : Vérifiez vos données.", fg="yellow")

Cela produit un message coloré en vert pour le succès et en jaune pour l’avertissement, directement à partir d’une seule ligne.

Prise en Charge des Entrées Utilisateur et des Mots de Passe

Section intitulée « Prise en Charge des Entrées Utilisateur et des Mots de Passe »

Dans certains cas, votre application CLI aura besoin d’interagir directement avec l’utilisateur pour obtenir des informations, demander une confirmation, ou même gérer des mots de passe. Click facilite ce type d’interactions avec des fonctions simples et pratiques.

Avec click.prompt(), vous pouvez demander à l’utilisateur de saisir une information. C’est idéal pour récupérer des données dynamiques lors de l’exécution.

import click
@click.command()
def demande_nom():
nom = click.prompt("Veuillez entrer votre nom")
click.echo(f"Bonjour, {nom}!")

En exécutant ce code, le programme demandera à l’utilisateur de saisir son nom, qui sera ensuite affiché dans un message personnalisé.

Click permet aussi de valider le type de données entrées par l’utilisateur avec click.prompt(). Par exemple, vous pouvez forcer l’entrée à être un entier :

@click.command()
def demander_age():
age = click.prompt("Veuillez entrer votre âge", type=int)
click.echo(f"Vous avez {age} ans.")

Si l’utilisateur entre une valeur qui n’est pas un entier, Click affichera automatiquement un message d’erreur et demandera une nouvelle saisie.

Pour des actions sensibles, il est souvent utile de demander une confirmation afin d’éviter les erreurs. click.confirm() permet de vérifier que l’utilisateur souhaite réellement effectuer l’action.

@click.command()
def supprimer_fichier():
if click.confirm("Êtes-vous sûr de vouloir supprimer le fichier ?"):
click.echo("Fichier supprimé.")
else:
click.echo("Action annulée.")

Ici, l’utilisateur doit confirmer (par yes ou no) avant que l’action ne soit exécutée.

Gestion des Mots de Passe avec click.password_option()

Section intitulée « Gestion des Mots de Passe avec click.password_option() »

Click propose également une option pour manipuler les mots de passe sans les afficher à l’écran, ce qui est essentiel pour la sécurité des informations sensibles.

@click.command()
@click.password_option()
def authentification(password):
if password == "mon_secret":
click.echo("Accès autorisé.")
else:
click.echo("Accès refusé.")

Dans cet exemple, click.password_option() masque l’entrée du mot de passe pour éviter qu’il soit visible. Vous pouvez aussi ajouter un message personnalisé pour guider l’utilisateur.

Pour garantir le bon fonctionnement de votre application CLI et faciliter sa maintenance, il est essentiel de tester vos commandes. Heureusement, Click offre une méthode efficace pour simuler des appels en ligne de commande avec CliRunner, un outil intégré qui permet de tester chaque fonction CLI comme si elle était appelée depuis le terminal.

CliRunner vous permet de simuler l’exécution d’une commande et de vérifier les sorties, sans avoir à réellement exécuter le code dans un terminal.

Voici un exemple de test pour une commande simple qui affiche un message de bienvenue :

import click
from click.testing import CliRunner
@click.command()
def bienvenue():
click.echo("Bienvenue dans notre application !")
def test_bienvenue():
runner = CliRunner()
result = runner.invoke(bienvenue)
assert result.exit_code == 0
assert "Bienvenue dans notre application !" in result.output

Dans cet exemple :

  • runner.invoke(bienvenue) exécute la commande bienvenue dans un environnement de test.
  • result.exit_code vérifie que la commande s’est terminée sans erreur (un code de sortie de 0 indique un succès).
  • result.output permet de vérifier que le message attendu est bien affiché.

Tester une Commande avec des Options et des Arguments

Section intitulée « Tester une Commande avec des Options et des Arguments »

CliRunner permet également de tester des commandes prenant des arguments et des options. Par exemple, pour tester une commande qui demande un nom :

@click.command()
@click.option('--name', default='Inconnu')
def saluer(name):
click.echo(f"Bonjour, {name} !")
def test_saluer():
runner = CliRunner()
result = runner.invoke(saluer, ['--name', 'Alice'])
assert result.exit_code == 0
assert "Bonjour, Alice !" in result.output

Dans cet exemple, --name est passé en argument à runner.invoke() pour simuler l’entrée de l’utilisateur.

Les tests ne se limitent pas aux cas de succès. Il est tout aussi important de vérifier les erreurs pour s’assurer que l’application guide correctement l’utilisateur en cas de mauvaise utilisation.

@click.command()
@click.argument('age', type=int)
def verifier_age(age):
if age < 18:
raise click.BadParameter("L'âge doit être d'au moins 18 ans.")
click.echo("Âge valide.")
def test_verifier_age():
runner = CliRunner()
result = runner.invoke(verifier_age, ['17'])
assert result.exit_code != 0
assert "L'âge doit être d'au moins 18 ans." in result.output

Ici, la commande verifier_age lève une erreur si l’utilisateur entre un âge inférieur à 18. Le test vérifie que le message d’erreur apparaît bien dans la sortie.

Pour conclure, Click est une librairie incontournable pour quiconque souhaite créer des applications en ligne de commande robustes, intuitives et élégantes en Python. Grâce à sa simplicité et sa flexibilité, elle permet de structurer des CLI, de gérer efficacement les erreurs, de styliser les messages et d’interagir avec les utilisateurs sans effort.

Que vous construisiez un petit script ou une application complexe avec de multiples commandes, Click simplifie chaque étape et favorise un code maintenable. En appliquant les bonnes pratiques que nous avons vues — structuration modulaire, tests approfondis, messages d’aide clairs et interactions utilisateur soignées —, vous pouvez offrir une expérience agréable et professionnelle à vos utilisateurs.

La puissance de Click réside dans son approche simple, mais riche en fonctionnalités, permettant aux développeurs de se concentrer sur l’essentiel : fournir un outil utile et efficace. Alors, à vos commandes !