Tom Daluzeau
← Parcours Python
Python · leçon 5 / 5Ch. 1 · Exceptions12 minTerminée

Exceptions : lire un traceback, try / except / else / finally

Lire un traceback dans le bon sens, attraper une exception avec try / except, savoir quand passent else et finally, et ranger les except du plus précis au plus général.

Mis à jour le

Pourquoi on voit ça

Un script DevOps travaille avec des choses qui échouent : un fichier absent, un port écrit "abc" dans un inventaire, un champ manquant dans un JSON. Quand ça arrive, Python lève une exception : il arrête ce qu’il faisait et affiche un traceback.

Deux compétences à avoir :

  1. lire un traceback, pour trouver la cause en dix secondes ;
  2. attraper une exception avec try / except, pour décider quoi faire au lieu de laisser le script s’arrêter.

Au contrôle, la réponse attendue est parfois une exception. Tu dois donc savoir quelle ligne plante, avec quel type, et quels blocs s’exécutent ensuite.

Lire un traceback : de bas en haut

Voici un script fleet.py :

def owner_of(server):
    return server["owner"]

servers = [{"name": "web-1", "owner": "alice"}, {"name": "db-1"}]
for s in servers:
    print(owner_of(s))
alice
Traceback (most recent call last):
  File "fleet.py", line 6, in <module>
    print(owner_of(s))
          ~~~~~~~~^^^
  File "fleet.py", line 2, in owner_of
    return server["owner"]
           ~~~~~~^^^^^^^^^
KeyError: 'owner'

Le réflexe : commence par la dernière ligne.

Où Ce que tu y lis
dernière ligne le type de l’exception et le message : KeyError: 'owner'
juste au-dessus la ligne qui a planté : server["owner"], ligne 2, dans owner_of
plus haut le chemin : qui a appelé qui (la ligne 6 a appelé owner_of)
Traceback (most recent call last): File "fleet.py", line 6, in <module> print(owner_of(s)) File "fleet.py", line 2, in owner_of return server["owner"] KeyError: 'owner' lis de bas en haut 3. qui a appelé ligne 6, dans le programme 2. la ligne qui a planté ligne 2, dans owner_of 1. type et message commence toujours ici
L'appel le plus récent est en bas : la cause est dans les deux dernières lignes.

« most recent call last » veut dire : l’appel le plus récent est en bas. Le haut du traceback est le point de départ, le bas est l’endroit de l’accident.

Remarque aussi que alice s’est affiché avant le traceback : le premier tour de boucle a réussi. C’est le deuxième serveur, db-1, qui n’a pas de clé owner.

Les exceptions courantes

Exception Quand Exemple
ValueError bon type, mais valeur inutilisable int("abc")
TypeError mauvais type "1" + 1
KeyError clé absente d’un dictionnaire (avec les crochets) s["owner"]
FileNotFoundError fichier introuvable open("absent.txt")

La différence entre ValueError et TypeError : int() accepte un texte, donc int("abc") a le bon type mais un mauvais contenu. À l’inverse, "1" + 1 mélange deux types que + ne sait pas combiner.

Attraper : try / except

try:
    port = int("abc")
    print("ok")
except ValueError:
    print("invalide")
print("suite")
invalide
suite

Déroule :

  1. Python essaie le bloc try.
  2. int("abc") lève ValueError. Le reste du try est sauté : print("ok") ne s’exécute pas.
  3. Python cherche un except qui correspond. except ValueError convient : son bloc s’exécute.
  4. Le programme continue après le bloc : suite s’affiche.

S’il n’y a pas d’exception, le try s’exécute en entier et le except est ignoré.

Un except n’attrape que son type

s = {"name": "web-1"}
try:
    print(s["owner"])
except ValueError:
    print("valeur")
print("suite")
Traceback (most recent call last):
  File "fleet.py", line 3, in <module>
    print(s["owner"])
          ~^^^^^^^^^
KeyError: 'owner'

L’exception levée est une KeyError. Le except ValueError ne la concerne pas. Elle n’est donc pas attrapée : le programme s’arrête, et suite ne s’affiche jamais.

else et finally

Le bloc complet a quatre parties.

Bloc S’exécute…
try toujours (jusqu’à la première exception)
except seulement si une exception de ce type est levée
else seulement s’il n’y a eu aucune exception
finally toujours, erreur ou pas
for texte in ["80", "abc"]:
    try:
        port = int(texte)
    except ValueError:
        print("invalide")
    else:
        print("ok", port)
    finally:
        print("fin")
ok 80
fin
invalide
fin
  • Avec "80" : pas d’exception → else, puis finally.
  • Avec "abc" : ValueError → except, puis finally. Le else est sauté.
seulement si le try a réussi try s'arrête à la 1re exception sans exception si exception else except ValueError si le type correspond finally toujours suite
else et except sont deux chemins qui s'excluent. finally est sur les deux.

À quoi ça sert : else contient la suite « quand tout va bien », et finally le ménage à faire dans tous les cas. finally passe même si l’exception n’est pas attrapée : il s’exécute, puis le traceback s’affiche.

Hiérarchie et ordre des except

Les exceptions sont rangées en famille. Exception est le parent de presque toutes les autres : ValueError, TypeError et KeyError sont ses enfants. FileNotFoundError est un enfant de OSError, lui-même enfant de Exception.

Exception ValueError TypeError KeyError OSError FileNotFoundError except OSError attrape aussi FileNotFoundError except Exception attrape tout ce qui est dessous
Un except attrape son type et tous ses enfants : le parent placé en premier vole tout.

Deux règles en découlent :

  • Un except attrape son type et tous ses enfants. except Exception attrape donc presque tout.
  • Python prend le premier except qui correspond, de haut en bas, et un seul. C’est la même logique que if / elif.

Donc si le parent est écrit en premier, les enfants en dessous ne servent à rien :

s = {"name": "web-1"}
try:
    print(s["owner"])
except Exception:
    print("erreur")
except KeyError:
    print("clé absente")
erreur

La règle : du plus précis au plus général.

s = {"name": "web-1"}
try:
    print(s["owner"])
except KeyError:
    print("clé absente")
except Exception:
    print("erreur")
clé absente

Contexte, pas à apprendre. En vrai, KeyError a un parent intermédiaire, LookupError, entre lui et Exception. Ça ne change rien aux deux règles.

À retenir

  • Un traceback se lit de bas en haut : la dernière ligne donne le type et le message.
  • Dès qu’une exception part, le reste du try est sauté.
  • Un except n’attrape que son type et ses enfants. Sinon, le programme s’arrête.
  • else : seulement s’il n’y a eu aucune exception. finally : toujours.
  • Les except se rangent du plus précis au plus général : except Exception en dernier.
  • ValueError : bon type, mauvaise valeur. TypeError : mauvais type. KeyError : clé absente. FileNotFoundError : fichier introuvable.

Entraînement

12 questions, dont 7 à compléter. Tu réponds, on passe à la suivante. La note arrive à la fin.

Cartes · 10

Réviser les cartes ici

Espace : retourner