Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 820 minDébutant

Erreurs et exceptions : lire un traceback, try et except

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • lire un traceback de bas en haut et trouver la cause d'une erreur ;
  • intercepter une exception précise avec try et except ;
  • lever une exception avec un message utile ;
  • terminer un script avec un code de retour testable.

3 exercices corrigés2 schémas

Une exception est l'objet que Python crée quand une instruction ne peut pas s'exécuter (fichier absent, conversion impossible, clé inconnue) ; si personne ne l'intercepte, le programme s'arrête et affiche un traceback, le rapport d'erreur. Ce chapitre apprend à lire un traceback, à reconnaître les exceptions les plus fréquentes, à les intercepter avec try et except, à en lever avec raise, et à terminer un script avec un code de retour qu'un autre programme peut tester.

Lire un traceback

Le script suivant calcule la moyenne des taux d'occupation lus dans un fichier, dont une ligne contient N/A :

python
# moyenne.py : moyenne des taux d'occupation lus dans un fichier
def lire_taux(ligne):
    nom, taux = ligne.strip().split(";")
    return float(taux)


def moyenne_occupation(chemin):
    with open(chemin, encoding="utf-8") as f:
        taux = [lire_taux(ligne) for ligne in f]
    return sum(taux) / len(taux)


print(moyenne_occupation("occupation.txt"))
text
Traceback (most recent call last):
  File "/home/etudiant/cours-python/moyenne.py", line 13, in <module>
    print(moyenne_occupation("occupation.txt"))
          ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/home/etudiant/cours-python/moyenne.py", line 9, in moyenne_occupation
    taux = [lire_taux(ligne) for ligne in f]
            ~~~~~~~~~^^^^^^^
  File "/home/etudiant/cours-python/moyenne.py", line 4, in lire_taux
    return float(taux)
ValueError: could not convert string to float: 'N/A'
Traceback (most recent call last):File "moyenne.py", line 13, in <module>print(moyenne_occupation("occupation.txt"))File "moyenne.py", line 9, in moyenne_occupationtaux = [lire_taux(ligne) for ligne in f]File "moyenne.py", line 4, in lire_tauxreturn float(taux)ValueError: could not convert string to float: 'N/A'sens de lecture1. Commence ici :le type et le message2. L'endroit exactde l'erreur3. Le chemin desappels qui y a mené
Un traceback se lit de bas en haut : l'erreur, l'endroit, puis le chemin des appels

Un traceback se lit de bas en haut :

  1. La dernière ligne donne le type de l'exception (ValueError) et son message : la conversion en flottant de la chaîne 'N/A' est impossible.
  2. Le bloc juste au-dessus montre où l'erreur s'est produite : fichier, numéro de ligne, fonction (lire_taux), et l'instruction elle-même.
  3. Les blocs supérieurs retracent le chemin des appels : le module a appelé moyenne_occupation (ligne 13), qui a appelé lire_taux (ligne 9). Les ^^^ et ~~~ soulignent la partie précise de l'instruction en cause.

Ici, le code est juste et c'est la donnée qui est invalide. Avant de corriger, demande-toi toujours lequel des deux est fautif : un traceback dans ton code ne veut pas dire que ton code est faux.

Les exceptions les plus fréquentes

python
>>> serveurs = ["srv-ad", "srv-ad2"]
>>> print(serveur)
Traceback (most recent call last):
  File "<python-input-1>", line 1, in <module>
    print(serveur)
          ^^^^^^^
NameError: name 'serveur' is not defined. Did you mean: 'serveurs'?
>>> serveurs[2]
Traceback (most recent call last):
  File "<python-input-2>", line 1, in <module>
    serveurs[2]
    ~~~~~~~~^^^
IndexError: list index out of range
>>> {"srv-ad": "192.168.10.177"}["srv-nas"]
Traceback (most recent call last):
  File "<python-input-3>", line 1, in <module>
    {"srv-ad": "192.168.10.177"}["srv-nas"]
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
KeyError: 'srv-nas'
>>> int("12 Go")
Traceback (most recent call last):
  File "<python-input-4>", line 1, in <module>
    int("12 Go")
    ~~~^^^^^^^^^
ValueError: invalid literal for int() with base 10: '12 Go'
>>> "Taux : " + 87.5
Traceback (most recent call last):
  File "<python-input-5>", line 1, in <module>
    "Taux : " + 87.5
    ~~~~~~~~~~^~~~~~
TypeError: can only concatenate str (not "float") to str
>>> serveurs.push("srv-nas")
Traceback (most recent call last):
  File "<python-input-6>", line 1, in <module>
    serveurs.push("srv-nas")
    ^^^^^^^^^^^^^
AttributeError: 'list' object has no attribute 'push'. Did you mean '.append'?
>>> 10 / 0
Traceback (most recent call last):
  File "<python-input-7>", line 1, in <module>
    10 / 0
    ~~~^~~
ZeroDivisionError: division by zero
>>> open("inventaire-2025.csv")
Traceback (most recent call last):
  File "<python-input-8>", line 1, in <module>
    open("inventaire-2025.csv")
    ~~~~^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'inventaire-2025.csv'
>>> import pathlb
Traceback (most recent call last):
  File "<python-input-9>", line 1, in <module>
    import pathlb
ModuleNotFoundError: No module named 'pathlb'. Did you mean: 'pathlib'?
ExceptionCause habituellePiste
NameErrorVariable ou fonction inconnue : faute de frappe, variable pas encore crééeLis la suggestion « Did you mean »
IndexErrorIndice hors de la listeLa liste a len(liste) éléments, d'indices 0 à len(liste) - 1
KeyErrorClé absente du dictionnaireget(), ou in avant d'accéder
ValueErrorBon type, mauvaise valeur : int("12 Go")Nettoie ou valide la donnée
TypeErrorOpération entre types incompatibles, mauvais nombre d'argumentsConvertis : str(87.5), ou une f-string
AttributeErrorMéthode qui n'existe pas pour ce type, souvent None.quelque_choseprint(type(x))
ZeroDivisionErrorDivision par zéro, souvent une moyenne de liste videTeste la longueur avant
FileNotFoundErrorFichier absent ou chemin relatif mal comprisVérifie le dossier courant, utilise Path(__file__).parent
PermissionErrorDroits insuffisants sur le fichierDroits du compte qui exécute le script
ModuleNotFoundErrorModule non installé, ou installé dans un autre environnementChapitre 2

Depuis Python 3.10, les messages suggèrent la correction (« Did you mean: 'serveurs'? ») ; Python 3.15 va plus loin et reconnaît les méthodes d'autres langages, comme le push du JavaScript.

try, except, else, finally

Pour traiter une erreur prévisible au lieu de laisser le script s'arrêter, place l'instruction risquée dans un bloc try et dis dans except quoi faire si une exception précise se produit :

python
# moyenne_robuste.py : ignorer les lignes invalides au lieu de s'arrêter
def moyenne_occupation(chemin):
    taux = []
    with open(chemin, encoding="utf-8") as f:
        for numero, ligne in enumerate(f, start=1):
            try:
                nom, valeur = ligne.strip().split(";")
                taux.append(float(valeur))
            except ValueError as erreur:
                print(f"Ligne {numero} ignorée ({ligne.strip()}) : {erreur}")
    return sum(taux) / len(taux)


for fichier in ["occupation.txt", "occupation-2025.txt"]:
    try:
        moyenne = moyenne_occupation(fichier)
    except FileNotFoundError:
        print(f"{fichier} : fichier introuvable")
    else:
        print(f"{fichier} : moyenne {moyenne:.1f} %")
    finally:
        print("--")
text
Ligne 3 ignorée (srv-web;N/A) : could not convert string to float: 'N/A'
occupation.txt : moyenne 70.7 %
--
occupation-2025.txt : fichier introuvable
--
tryle code surveilléexception ?nonelsetout s'est bien passéouiun exceptcorrespond ?ouiexcepttraite l'erreurnonaucun exceptne correspondfinallytoujours exécutéla suite du programmeou l'exception remonte
Le chemin d'exécution d'un bloc try : except en cas d'erreur, else sinon, finally toujours
BlocQuand il s'exécute
tryToujours : c'est le code surveillé. Garde-le court : seulement l'instruction qui peut échouer
except TypeErreur as eSi une exception de ce type (ou d'un sous-type) se produit dans le try ; e contient l'exception, str(e) son message
elseSi le try s'est terminé sans exception
finallyDans tous les cas, même après un return ou une exception non interceptée : libérer une ressource, écrire un journal

Plusieurs except peuvent se suivre, du plus précis au plus général ; except (ValueError, KeyError): en traite deux de la même façon. Le premier except qui correspond gagne.

Lever une exception avec raise

Une fonction qui reçoit des valeurs absurdes doit le dire, plutôt que de renvoyer un résultat faux. raise lève une exception ; tu peux utiliser les exceptions existantes (ValueError pour une valeur invalide) ou créer la tienne en héritant d'Exception :

python
# raise_demo.py : refuser des paramètres incohérents, avec une exception à soi
class ConfigurationInvalide(Exception):
    """Erreur dans le fichier de configuration du contrôle des disques."""


def verifier_seuils(alerte, critique):
    if not 0 < alerte < critique <= 100:
        raise ConfigurationInvalide(
            f"seuils incohérents : alerte={alerte}, critique={critique}"
        )


for alerte, critique in [(80, 90), (99, 90)]:
    try:
        verifier_seuils(alerte, critique)
        print(f"Seuils {alerte}/{critique} acceptés")
    except ConfigurationInvalide as erreur:
        print("Erreur :", erreur)

verifier_seuils(95, 90)
text
Seuils 80/90 acceptés
Erreur : seuils incohérents : alerte=99, critique=90
Traceback (most recent call last):
  File "/home/etudiant/cours-python/raise_demo.py", line 20, in <module>
    verifier_seuils(95, 90)
    ~~~~~~~~~~~~~~~^^^^^^^^
  File "/home/etudiant/cours-python/raise_demo.py", line 8, in verifier_seuils
    raise ConfigurationInvalide(
        f"seuils incohérents : alerte={alerte}, critique={critique}"
    )
ConfigurationInvalide: seuils incohérents : alerte=95, critique=90

Une exception à toi, comme ConfigurationInvalide, permet à l'appelant de la distinguer des autres. Le message doit dire quoi est faux et avec quelles valeurs : « seuils incohérents : alerte=95, critique=90 » se corrige en dix secondes, « erreur de configuration » en dix minutes. La classe ne contient qu'une docstring : l'héritage est expliqué au chapitre 14.

Code de retour d'un script

Un programme qui se termine renvoie au système un code de retour : 0 si tout s'est bien passé, un autre nombre sinon. Le Planificateur de tâches, cron, un pipeline d'intégration continue ou un autre script lisent ce code pour savoir si la tâche a réussi. sys.exit(code) termine le script avec le code choisi ; un script qui s'arrête sur une exception non interceptée renvoie 1.

python
# verif.py : un script qui renvoie un code de retour
import sys
from pathlib import Path

fichier = Path("inventaire.csv")
if not fichier.exists():
    print(f"ERREUR : {fichier} introuvable", file=sys.stderr)
    sys.exit(2)

print("Inventaire présent")
sys.exit(0)
bash
python verif.py
echo "Code de retour : $?"            # sous Linux ; sous PowerShell : $LASTEXITCODE
text
ERREUR : inventaire.csv introuvable
Code de retour : 2

Les messages d'erreur vont sur la sortie d'erreur (file=sys.stderr), séparée de la sortie normale : quand la sortie du script est redirigée dans un fichier, les erreurs restent visibles. Le même principe existe en PowerShell et en Bash (gestion des erreurs dans un script).

Exercices

Exercice 1 sur 3 Quelle exception ? Réussi À revoir

Pour chaque ligne, donne le type d'exception levée, sans exécuter. serveurs vaut ["srv-ad", "srv-ad2"] et ips vaut {"srv-ad": "192.168.10.177"}.

  1. serveurs[len(serveurs)]
  2. ips["SRV-AD"]
  3. float("87,5")
  4. len(42)
  5. sum([]) / len([])
  6. ips.append("srv-nas")
Voir la réponseMasquer la réponse de l'exercice 1
  1. IndexError : la liste a 2 éléments, d'indices 0 et 1 ; len(serveurs) vaut 2.
  2. KeyError: 'SRV-AD' : les clés sont sensibles à la casse.
  3. ValueError : Python attend un point décimal. Une valeur venue d'un tableur français se convertit avec float(valeur.replace(",", ".")).
  4. TypeError: object of type 'int' has no len().
  5. ZeroDivisionError : la moyenne d'une liste vide. Teste if liste: avant de diviser.
  6. AttributeError : un dictionnaire n'a pas de méthode append ; on ajoute avec ips["srv-nas"] = "192.168.10.183".

Tu avais trouvé ?

Exercice 2 sur 3 Une saisie robuste Réussi À revoir

Écris un script qui demande un numéro de VLAN et redemande tant que la saisie n'est pas un entier de 1 à 4094, avec un message différent selon que la saisie n'est pas un nombre ou est hors plage. Teste avec abc, 5000, puis 21.

Voir la réponseMasquer la réponse de l'exercice 2
python
# ex_vlan.py : redemander tant que la saisie n'est pas un numéro de VLAN valide
while True:
    saisie = input("Numéro de VLAN (1 à 4094) ? ")
    try:
        vlan = int(saisie)
    except ValueError:
        print(f"« {saisie} » n'est pas un nombre entier.")
        continue
    if 1 <= vlan <= 4094:
        break
    print(f"{vlan} est hors de la plage 1 à 4094.")

print(f"VLAN {vlan} retenu.")
text
Numéro de VLAN (1 à 4094) ? abc
« abc » n'est pas un nombre entier.
Numéro de VLAN (1 à 4094) ? 5000
5000 est hors de la plage 1 à 4094.
Numéro de VLAN (1 à 4094) ? 21
VLAN 21 retenu.

while True boucle jusqu'au break. Le try ne contient que la conversion, la seule instruction qui peut lever ValueError ; la vérification de la plage est un simple if. Les identifiants 0 et 4095 sont réservés par la norme 802.1Q, d'où la plage 1 à 4094.

Tu avais trouvé ?

Exercice 3 sur 3 L'erreur avalée Réussi À revoir

Ce script de contrôle affiche 0 machines à contrôler, alors que l'inventaire en compte 8. Il tourne pourtant chaque nuit « sans erreur ». Trouve le problème et corrige-le.

python
import csv


def charger_inventaire(chemin):
    try:
        with open(chemin, encoding="utf-8-sig", newline="") as f:
            return list(csv.DictReader(f, delimiter=";"))
    except:
        return []


machines = charger_inventaire("inventaire.csv")
print(len(machines), "machines à contrôler")
Voir la réponseMasquer la réponse de l'exercice 3

Le except: nu avale toutes les erreurs : ici, le fichier n'est pas dans le dossier courant de la tâche planifiée, open() lève FileNotFoundError, et la fonction renvoie une liste vide comme si l'inventaire était vide. Le script se termine avec le code 0, la tâche est « réussie », et plus rien n'est contrôlé.

text
0 machines à contrôler
Code de retour : 0

La correction retire le try de la fonction, qui laisse remonter l'erreur, et la traite là où on sait quoi faire : arrêter le script avec un message clair et un code de retour non nul, que la supervision verra.

python
import csv
import sys


def charger_inventaire(chemin):
    with open(chemin, encoding="utf-8-sig", newline="") as f:
        return list(csv.DictReader(f, delimiter=";"))


try:
    machines = charger_inventaire("inventaire.csv")
except FileNotFoundError as erreur:
    print(f"ERREUR : {erreur}", file=sys.stderr)
    sys.exit(1)

print(len(machines), "machines à contrôler")
text
ERREUR : [Errno 2] No such file or directory: 'inventaire.csv'
Code de retour : 1

Reste à rendre le chemin indépendant du dossier courant : Path(__file__).parent / "inventaire.csv".

Tu avais trouvé ?

Chapitre terminé !

Fais les 3 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : La bibliothèque standard pour l'admin : os, shutil, subprocess, datetimeRevoir le sommaire du cours