Typer : transforme ton script Python en vraie commande de terminal

Typer : transforme ton script Python en vraie commande de terminal

De quoi avez-vous besoin

Version de Python

3.x

Packages

  • {"nom":"typer","version":"0.15+"}

Difficulté

Débutant

Un script Python, c'est bien. Une vraie commande de terminal avec ses options, sa documentation et ses sous-commandes, c'est mieux. C'est exactement ce que fait Typer, la bibliothèque signée Sebastián Ramírez, le créateur de FastAPI. Le principe tient en une phrase : tu écris une fonction Python normale, tu ajoutes des annotations de type, et Typer s'occupe du reste. Parsing des arguments, messages d'aide, validation des types, auto-complétion : tout sort tout seul.

Aujourd'hui, on transforme un script basique en petite boîte à outils utilisable par n'importe qui dans un terminal. Aucune configuration XML, aucun décorateur illisible. C'est parti.

Étape 1 : l'installation

Comme d'habitude, un environnement virtuel et un pip install. On prend l'option [all] pour embarquer aussi Rich, qui rend la sortie du terminal bien plus lisible (couleurs, tableaux, barres de progression).

bash
python -m venv venv
source venv/bin/activate   # sous Windows : venv\Scripts\activate
pip install "typer[all]"

Étape 2 : le strict minimum

Crée un fichier app.py avec ce contenu :

python
import typer

app = typer.Typer()


@app.command()
def bonjour(nom: str = typer.Argument("humain")):
    """Dit bonjour, en français."""
    typer.echo(f"Bonjour, {nom} !")


if __name__ == "__main__":
    app()

Trois choses à retenir. app = typer.Typer() crée l'application. Le décorateur @app.command() transforme une fonction en commande. Et typer.echo() affiche proprement dans le terminal. Lance python app.py bonjour et tu obtiens Bonjour, humain !. La docstring de la fonction devient automatiquement l'aide affichée par --help. Typer lit aussi les annotations : nom est une str, et il le sait.

Étape 3 : arguments positionnels et validation gratuite

Ajoute une commande qui prend un nombre en argument :

python
@app.command()
def doubler(nombre: int):
    """Double un nombre entier."""
    typer.echo(f"{nombre} x 2 = {nombre * 2}")

Le type hint int fait le sale boulot à ta place. python app.py doubler 21 affiche 21 x 2 = 42, et python app.py doubler abc te renvoie une erreur claire du genre Invalid value for NOMBRE. Pas besoin d'écrire un if isinstance(...) à la main. C'est ça, la magie Typer : le typage devient l'interface.

Étape 4 : les options (--fort, -f)

Un booléen avec une valeur par défaut se transforme en drapeau :

python
@app.command()
def saluer(
    nom: str,
    fort: bool = typer.Option(False, "--fort", "-f", help="Crie en MAJUSCULES"),
):
    """Saluer quelqu'un, avec option pour crier."""
    message = f"Bonjour, {nom} !"
    if fort:
        message = message.upper()
    typer.echo(message)

Par défaut, fort vaut False. Il passe à True dès que tu tapes --fort ou -f, et le help= alimente l'aide. Teste : python app.py saluer Lea puis python app.py saluer Lea --fort. La deuxième version crie en majuscules. On sent déjà la structure d'un vrai outil.

Étape 5 : les sous-commandes, le vrai intérêt

Quand ton outil fait plusieurs choses, tu ne veux pas un gros switch illisible. Typer gère les sous-commandes naturellement : chaque fonction décorée devient une commande séparée.

python
@app.command()
def repeter(mot: str, fois: int = typer.Option(3, "--fois", "-n")):
    """Répète un mot plusieurs fois."""
    typer.echo(" ".join([mot] * fois))

python app.py repeter salut --fois 4 affiche salut salut salut salut. Regarde maintenant le --help de l'application : chaque docstring devient une ligne de documentation, classée par commande. Ton script est devenu un outil documenté sans effort supplémentaire.

Étape 6 : erreurs propres et codes de sortie

Une CLI correcte doit échouer proprement, surtout si un autre programme l'appelle. Typer fournit typer.Exit pour renvoyer un code de sortie non nul, et typer.echo(..., err=True) pour écrire sur la sortie d'erreur.

python
@app.command()
def diviser(a: float, b: float = typer.Option(..., "--diviseur", "-d")):
    """Divise a par b (b obligatoire)."""
    if b == 0:
        typer.echo("Division par zéro, non merci.", err=True)
        raise typer.Exit(code=1)
    typer.echo(f"{a} / {b} = {a / b}")

Le ... dans typer.Option(...) rend l'option obligatoire : Typer te la réclame si tu l'oublies. Et si quelqu'un passe --diviseur 0, le script s'arrête avec le code 1 au lieu de lever une traceback moche. C'est exactement le comportement qu'attend un script exécuté dans un pipeline.

Bonus : l'auto-complétion

Petit luxe appréciable : Typer embarque la complétion pour bash, zsh et fish. python app.py --install-completion te donne la marche à suivre. Une fois installée, tu tapes app.py re puis Tab, et le shell complète repeter tout seul. Ça change la vie au quotidien.

Conclusion

En vingt minutes, ton script Python est devenu une vraie commande : arguments typés, options, sous-commandes, aide générée, erreurs propres et auto-complétion. Le tout sans framework lourd. Typer suit la même philosophie que FastAPI : laisse le typage Python faire le travail à ta place. Prochaine étape logique : brancher cet outil sur une API ou un modèle local, et l'emballer dans une commande que toute ton équipe peut lancer. Le terminal redevient un endroit où il fait bon vivre.