Aller au contenu

La gestion des erreurs

Introduction

Comme en C#, Python utilise des exceptions pour signaler les erreurs rencontrées pendant l'exécution d'un programme. Une exception interrompt le déroulement lors de l'exécution du code, et se propage jusqu'à un gestionnaire capable de la traiter. Si elle n'est pas interceptée, l'exécution s'interrompt et Python affiche les informations concernant l'erreur rencontrée.

D'un point de vue terminologie, on dit lever une exception pour la déclencher et d'intercepter une exception pour la traiter.

Intercepter une exception

En Python, les mots-clés try et except correspondent à try et catch en C# :

text = "abc"

try:
    number = int(text)
    print(number)
except ValueError as error:
    print(f"Conversion impossible : {error}")

Le bloc try contient les instructions susceptibles de lever une exception. Le bloc except précise le type d'exception à intercepter et le traitement à exécuter. En accord avec les bonnes pratiques, il est préférable d'intercepter des types d'exceptions précis. Un bloc except: sans type intercepte également les exceptions liées à l'interruption de l'exécution du code, comme KeyboardInterrupt, et cela doit être évité.

Dans cet exemple, la conversion échoue et lève une exception de type ValueError. as error permet d'accéder à l'objet exception et est facultatif si cet objet n'est pas utilisé.

Voici l'équivalent en C# :

string text = "abc";

try
{
    int number = int.Parse(text);
    Console.WriteLine(number);
}
catch (FormatException error)
{
    Console.WriteLine($"Conversion impossible : {error.Message}");
}

Intercepter plusieurs types d'exceptions

Plusieurs blocs except peuvent traiter différemment les exceptions :

try:
    number = int(input("Saisissez un entier : "))
    result = 100 / number
    print(result)
except ValueError:
    print("La valeur saisie n'est pas un entier.")
except ZeroDivisionError:
    print("La division par zéro est impossible.")

Un seul bloc except est exécuté : le premier dont le type correspond à l'exception levée. Les types les plus spécifiques doivent donc apparaître avant leurs classes parentes.

Pour appliquer le même traitement à plusieurs types, nous les regroupons dans un tuple :

except (ValueError, ZeroDivisionError) as error:
    print(f"Calcul impossible : {error}")

Voici quelques exceptions couramment utilisées :

Exception Signification
ValueError Une valeur est inappropriée pour l'opération demandée.
TypeError Un objet possède un type incompatible avec l'opération demandée.
ZeroDivisionError Une division par zéro est effectuée.
IndexError Un indice se trouve en dehors des limites d'une séquence.
KeyError Une clé est absente d'un dictionnaire.
FileNotFoundError Un fichier ou un répertoire demandé est introuvable.

Exécuter du code en cas de réussite

Python propose un bloc else, exécuté lorsque le bloc try se termine normalement, sans exception ni sortie anticipée par une instruction return, break ou continue.

try:
    number = int("42")
except ValueError:
    print("Conversion impossible.")
else:
    print(f"Conversion réussie : {number}")

Il permet de distinguer le traitement susceptible d'échouer du traitement à effectuer après sa réussite. Une exception levée dans le bloc else n'est pas interceptée par les blocs except associés à ce try.

Exécuter un traitement de "finalisation"

Comme en C#, le bloc finally permet d'exécuter un traitement à la sortie du bloc try, qu'une exception ait été levée ou non. Il s'exécute également lorsqu'une instruction return met fin à l'exécution de la fonction.

try:
    number = int("abc")
except ValueError:
    print("Conversion impossible.")
finally:
    print("Fin du traitement.")

Pour les ressources telles que les fichiers, il existe l'instruction with, comparable à using en C# :

try:
    with open("data.txt", encoding="utf-8") as file:
        content = file.read()
except OSError as error:
    print(f"Lecture impossible : {error}")

Le fichier est automatiquement fermé à la sortie du bloc with, même si une exception survient pendant sa lecture.

Lever une exception

Le mot-clé raise correspond à throw en C#. Il permet de signaler explicitement une erreur :

def calculate_total(price: float, quantity: int) -> float:
    if price < 0:
        raise ValueError("Le prix ne peut pas être négatif.")

    if quantity <= 0:
        raise ValueError("La quantité doit être strictement positive.")

    return price * quantity

L'appelant peut intercepter cette exception :

try:
    total = calculate_total(10.0, 0)
except ValueError as error:
    print(error)

Relancer une exception

Dans un bloc except, l'instruction raise sans argument relance l'exception en cours. Elle correspond à l'utilisation de throw; en C# et préserve les informations d'origine sur l'erreur rencontrée.

try:
    number = int("abc")
except ValueError as error:
    print(f"Erreur de conversion : {error}")
    raise

Ce mécanisme permet de loguer une erreur avant qu'elle remonte à l'appelant.

Implémenter une exception personnalisée

Une exception personnalisée est une classe qui hérite de Exception :

class InsufficientStockError(Exception):
    pass

Le mot-clé pass indique ici que la classe n'ajoute aucun comportement à celui de sa classe parente.

def check_stock(stock: int, quantity: int) -> None:
    if quantity > stock:
        raise InsufficientStockError(
            "Le stock disponible est insuffisant."
        )

Cette exception peut ensuite être interceptée par son type :

try:
    check_stock(5, 10)
except InsufficientStockError as error:
    print(error)

En Python, les noms d'exceptions se terminent généralement par Error, tandis que le suffixe Exception est habituel en C#.

Conserver la cause d'une exception

Lorsqu'une erreur technique est remplacée par une exception plus adaptée au contexte, la syntaxe raise ... from ... permet d'indiquer la cause de manière explicite :

try:
    quantity = int("abc")
except ValueError as error:
    raise ValueError("La quantité saisie est invalide.") from error

Ce mécanisme est comparable à l'utilisation de InnerException en C#. La cause est accessible avec l'attribut __cause__.

Récapitulatif des correspondances entre Python et C#

C# Python
try try
catch except
finally finally
throw new ... raise ...
throw; raise sans argument
using with pour les gestionnaires de contexte
InnerException __cause__ avec raise ... from ...
catch (Exception ex) except Exception as error