Aller au contenu

Créer une CLI Python avec Click

Mise à jour :

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 ?

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.

Installation de Click

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.

Terminal window
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 :

Terminal window
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 ?

Premier Pas : Création d’une CLI de base

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 :

Terminal window
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!

Gestion des Arguments et Options

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.

Ajouter des Arguments

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 :

Terminal window
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.

Ajouter des Options

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 :

Terminal window
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.

Groupes de Commandes : Organiser une CLI Complexe

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 :

Terminal window
python service.py start

Cela affiche :

Service démarré

Et pour vérifier le statut :

Terminal window
python service.py status

Résultat :

Service en cours de fonctionnement

Ajouter des Descriptions et de l’Aide

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é")

Validation des entrées

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.

Validation des Types

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.

Limiter les Choix avec click.Choice

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.

Gestion des Fichiers avec click.File

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.

Gestion d’Erreurs Personnalisées

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.

Styliser et Formater la Sortie

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.

Utiliser click.echo() pour Afficher des Messages

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.

Ajouter du Style avec click.style()

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.

click.secho() pour un Style Simplifié

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

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.

Utiliser click.prompt() pour Demander une Entrée

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é.

Validation des Entrées

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.

Demander une Confirmation avec click.confirm()

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()

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.

Testing de la CLI avec Click

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.

Utiliser CliRunner pour Tester une Commande

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

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.

Tester les Messages d’Erreur

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.

Conclusion

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 !

Liens Utiles