0% ont trouvé ce document utile (0 vote)
0 vues23 pages

Formation Python Exceptions

La formation Python sur la gestion des exceptions couvre les concepts essentiels tels que les structures try, except, else, finally et raise. Elle aborde l'importance de la gestion des exceptions pour créer des programmes robustes, en fournissant des exemples pratiques dans des domaines comme la finance et le e-commerce. Le module inclut également des bonnes pratiques et un projet professionnel complet pour renforcer les compétences acquises.

Transféré par

Anas Mounchit
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
0 vues23 pages

Formation Python Exceptions

La formation Python sur la gestion des exceptions couvre les concepts essentiels tels que les structures try, except, else, finally et raise. Elle aborde l'importance de la gestion des exceptions pour créer des programmes robustes, en fournissant des exemples pratiques dans des domaines comme la finance et le e-commerce. Le module inclut également des bonnes pratiques et un projet professionnel complet pour renforcer les compétences acquises.

Transféré par

Anas Mounchit
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

■ FORMATION PYTHON PROFESSIONNELLE

Gestion des Exceptions


& Robustesse du Code
try · except · else · finally · raise · exceptions personnalisées

■ Au programme

■ Chapitre 1 · Pourquoi les exceptions sont inévitables

■ Chapitre 2 · Les exceptions Python les plus courantes

■ Chapitre 3 · La structure try...except

■ Chapitre 4 · Les clauses else et finally

■ Chapitre 5 · Lever ses propres exceptions avec raise

■ Chapitre 6 · Bonnes pratiques & application QA/Selenium

■ Chapitre 7 · Projet professionnel complet

Niveau : Intermédiaire | Exemples : Finance, RH, E-commerce, QA / Selenium

© Formation Python Professionnelle Page 1


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

Introduction

Jusqu'ici, nos programmes supposaient que tout se passait toujours comme prévu. Mais dans la
réalité professionnelle, c'est rarement le cas. Les utilisateurs font des erreurs de saisie, des fichiers
sont manquants, des connexions réseau échouent, des serveurs ne répondent pas. Un programme
qui plante au moindre imprévu n'est pas un programme professionnel.

La gestion des exceptions est l'art de prévoir ces imprévus et d'y répondre avec élégance — sans
arrêt brutal du programme. C'est une compétence centrale pour tout développeur Python, et
particulièrement critique en QA (Quality Assurance) où des tests fragiles produisent des rapports
d'erreur inutilisables.

■■ Ce module couvre la gestion des exceptions de A à Z avec des exemples professionnels


réels issus de la finance, du e-commerce, des RH et des tests automatisés avec Selenium.

© Formation Python Professionnelle Page 2


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

CHAPITRE 1

Pourquoi les Exceptions sont Inévitables


La réalité de l'exécution d'un programme en production

Le monde réel est imprévisible


Un programme en production interagit constamment avec l'extérieur : des utilisateurs humains qui
font des erreurs, des fichiers qui peuvent ne pas exister, des bases de données qui peuvent être
indisponibles, des APIs qui peuvent renvoyer des données inattendues. Aucun code, même le plus
rigoureux, ne peut contrôler ces facteurs externes.

Ce qui se passe SANS gestion des exceptions

■ sans_exceptions.py

# ■■■ Scénario : calculatrice de conversion de devise ■■■■■■■■■■■■■■


taux_euro_mad = 10.85
montant_str = input('Montant en EUR à convertir : ')
montant = float(montant_str) # ← point de danger n°1
resultat = montant / 0 # ← point de danger n°2
print(f'Résultat : {resultat} MAD')

■ Sortie (crash)

# Si l'utilisateur tape 'abc' :


ValueError: could not convert string to float: 'abc'
Traceback (most recent call last):
File "[Link]", line 5, in <module>
montant = float(montant_str)
# Le programme s'arrête BRUTALEMENT. L'utilisateur voit une erreur
technique.
# En production, c'est inacceptable.

■ Un programme qui plante avec un Traceback brut est une mauvaise expérience utilisateur
ET une faille de sécurité potentielle — le stack trace révèle la structure interne de votre
application à l'extérieur.

© Formation Python Professionnelle Page 3


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

CHAPITRE 2

Les Exceptions Python les Plus Courantes


Identifier le bon type d'erreur pour mieux la gérer

Python dispose d'une hiérarchie d'exceptions intégrées. Connaître les plus courantes est
indispensable pour écrire des blocs except précis et efficaces.

Exception Déclenchée quand... Exemple concret

ValueError int('abc') → impossible de convertir


Type correct, mais valeur inappropriée

TypeError Opération sur un type incompatible '5' + 3 → str + int impossible

IndexError Index hors limites d'une séquence liste[10] sur une liste de 5 éléments

KeyError Clé inexistante dans un dictionnaire mon_dict['email'] si 'email' absent

FileNotFoundError Fichier ou répertoire introuvable open('[Link]') si fichier absent

ZeroDivisionError Division ou modulo par zéro 100 / 0 ou 100 % 0

AttributeError Attribut ou méthode inexistant sur un [Link]()


objet → NoneType n'a pas upper()

ImportError Module introuvable à l'import import selenium si non installé

PermissionError Droits insuffisants sur un fichier/dossier


Écrire dans un fichier en lecture seule

TimeoutError Délai d'attente dépassé (réseau, I/O) Connexion API qui ne répond pas

Exception Classe parente de toutes les exceptions


Utilisée comme filet de sécurité générique

Visualisation de la hiérarchie
Toutes les exceptions héritent de BaseException. Comprendre cette hiérarchie explique pourquoi attraper
Exception intercepte aussi ValueError, TypeError, etc.

■ Hiérarchie des exceptions Python

BaseException
■■■ Exception ← attrape presque tout
■■■ ValueError ← mauvaise valeur
■■■ TypeError ← mauvais type
■■■ IndexError ← index hors limites
■■■ KeyError ← clé manquante
■■■ FileNotFoundError ← fichier absent
■■■ ZeroDivisionError ← division par zéro
■■■ AttributeError ← attribut manquant
■■■ ImportError ← module absent

© Formation Python Professionnelle Page 4


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

■■■ ... (50+ autres) ← exceptions spécialisées

■ Exemple professionnel n°1 : Identifier le bon type d'erreur

■ identifier_exceptions.py

# ■■■ Diagnostic d'exceptions – Système de Paie


■■■■■■■■■■■■■■■■■■■■■
employes = {'E001': 4500, 'E002': 3200, 'E003': 5800}
heures = [8, 7.5, 'N/A', 6, 10] # données corrompues !
jours = 0 # erreur de configuration
# TypeError – mauvais type dans le calcul
try:
total = heures[2] * 25 # 'N/A' * 25 → TypeError
except TypeError as e:
print(f'TypeError : {e}')
# IndexError – index hors limites
try:
print(heures[99])
except IndexError as e:
print(f'IndexError : {e}')
# KeyError – clé manquante dans le dict
try:
salaire = employes['E999']
except KeyError as e:
print(f'KeyError : {e}')
# ZeroDivisionError – division par zéro
try:
moyenne = sum([Link]()) / jours
except ZeroDivisionError as e:
print(f'ZeroDivisionError : {e}')

■ Sortie console

TypeError : can't multiply sequence by non-int of type 'str'


IndexError : list index out of range
KeyError : 'E999'
ZeroDivisionError : division by zero

© Formation Python Professionnelle Page 5


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

CHAPITRE 3

La Structure try...except
Attraper les erreurs avant qu'elles ne crashent le programme

Syntaxe et fonctionnement

■ Syntaxe de base

try:
# ■■ Bloc TRY
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
# Code risqué : peut lever une exception
instruction_risquee()
except NomException:
# ■■ Bloc EXCEPT
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
# Exécuté UNIQUEMENT si NomException est levée dans le try
gerer_l_erreur()

Approche 1 : plusieurs blocs except (recommandée)


Utilisez plusieurs blocs except quand chaque type d'erreur nécessite un traitement différent. Python
teste les blocs dans l'ordre et s'arrête au premier qui correspond.

■ approche_multiple_except.py

# ■■■ Calculatrice de Conversion Devise – Version Robuste


■■■■■■■■■■■
def convertir_devise(montant_str, taux, devise_cible='MAD'):
"""Convertit un montant EUR vers une devise cible."""
try:
montant = float(montant_str) # risque : ValueError
resultat = montant / taux # risque : ZeroDivisionError
return round(resultat, 2)
except ValueError:
# Traitement spécifique : saisie non numérique
print(f"■ '{montant_str}' n'est pas un nombre valide.")
return None
except ZeroDivisionError:
# Traitement spécifique : taux = 0 (configuration invalide)
print('■ Taux de conversion invalide (zéro).')
return None

© Formation Python Professionnelle Page 6


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

# ■■ Tests ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
■■■■■■■■■■■
print(convertir_devise('250', 10.85))
# → 23.04 MAD
print(convertir_devise('abc', 10.85))
# → ValueError géré
print(convertir_devise('250', 0))
# → ZeroDivisionError géré

Approche 2 : tuple d'exceptions + capture de l'objet


Quand plusieurs exceptions provoquent le même traitement, regroupez-les dans un tuple. La clause
as e capture l'objet exception pour accéder au message d'erreur précis.

■ tuple_exceptions.py

try:
montant = float(input('Montant EUR : '))
resultat = montant / float(input('Taux : '))
print(f'Résultat : {resultat:.2f} MAD')
except (ValueError, ZeroDivisionError) as e:
# as e → capture le message d'erreur exact de Python
print(f'■ Erreur de calcul : {e}')
print(' Veuillez réessayer avec des valeurs valides.')

■■ La clause 'as e' est très utile pour le logging (journalisation). En production, on enregistre
souvent 'str(e)' ou 'repr(e)' dans un fichier de log plutôt que d'afficher l'erreur à l'utilisateur.

■ Exemple professionnel n°2 : Import de données CSV – Département Finance

■ import_csv_securise.py

# ■■■ Import CSV Sécurisé – Rapport Financier


■■■■■■■■■■■■■■■■■■■■■■■
import csv
def importer_transactions(chemin_fichier):
"""
Importe les transactions depuis un fichier CSV.
Retourne une liste de dict ou None en cas d'erreur.
"""
transactions = []
try:
with open(chemin_fichier, 'r', encoding='utf-8') as f:
lecteur = [Link](f)

© Formation Python Professionnelle Page 7


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

for ligne in lecteur:


montant = float(ligne['montant'])
[Link]({
'id' : ligne['id'],
'client' : ligne['client'],
'montant': montant,
'statut' : ligne['statut']
})
return transactions
except FileNotFoundError:
print(f'■ Fichier introuvable : {chemin_fichier}')
print(' Vérifiez le chemin et relancez l\'import.')
return None
except KeyError as e:
print(f'■ Colonne manquante dans le CSV : {e}')
print(' Colonnes attendues : id, client, montant, statut')
return None
except ValueError as e:
print(f'■ Valeur non numérique dans la colonne montant : {e}')
return None
# ■■ Utilisation
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
data = importer_transactions('transactions_juin_2025.csv')
if data:
print(f'■ {len(data)} transactions importées.')
total = sum(t['montant'] for t in data)
print(f' Total : {total:,.2f} MAD')

© Formation Python Professionnelle Page 8


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

CHAPITRE 4

Aller Plus Loin : else et finally


La structure complète try · except · else · finally

La structure complète

■ Structure complète

try:
# Code risqué
operation_risquee()
except UneException:
# Exécuté SI une UneException est levée
gerer_erreur()
else:
# Exécuté UNIQUEMENT si AUCUNE exception n'a été levée
# Idéal pour le code qui dépend du succès de try
traiter_resultat()
finally:
# Exécuté TOUJOURS – avec ou sans exception
# Nettoyage : fermer fichier, connexion DB, session navigateur...
nettoyer_ressources()

Clause Quand s'exécute-t-elle ? Usage typique

try Toujours (code principal) Code risqué à surveiller

except Uniquement si exception levée Gérer l'erreur, logger, fallback

else Uniquement si AUCUNE exception Code post-succès (traitement du résultat)

finally TOUJOURS (avec ou sans exception) Nettoyage : close(), déconnexion, screenshot

■ Exemple professionnel n°3 : Lecture de fichier de configuration

■ charger_config.py

# ■■■ Lecture Config Sécurisée – Application de Paie


■■■■■■■■■■■■■■■
import json
def charger_configuration(chemin):
"""
Charge un fichier JSON de configuration.
Illustre l'usage complet de try/except/else/finally.

© Formation Python Professionnelle Page 9


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

"""
fichier = None
try:
fichier = open(chemin, 'r', encoding='utf-8')
config = [Link](fichier)
except FileNotFoundError:
print(f'■ Config introuvable : {chemin}')
print(' Chargement de la configuration par défaut...')
return {'taux_cnss': 0.0448, 'taux_amo': 0.0226, 'devise': 'MAD'}
except [Link] as e:
print(f'■ Fichier JSON corrompu : {e}')
return None
else:
# S'exécute UNIQUEMENT si open() ET [Link]() ont réussi
print(f'■ Configuration chargée : {len(config)} paramètres.')
return config
finally:
# S'exécute TOUJOURS – on ferme le fichier quoi qu'il arrive
if fichier:
[Link]()
print(' Fichier de config fermé proprement.')
# ■■ Tests ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
■■■■■■■■■■■
config = charger_configuration('config_paie.json')
if config:
print(f"Devise configurée : {[Link]('devise', 'MAD')}")

■ Sorties console (2 scénarios)

# Cas 1 : fichier présent et valide


■ Configuration chargée : 5 paramètres.
Fichier de config fermé proprement.
Devise configurée : MAD
# Cas 2 : fichier absent
■ Config introuvable : config_paie.json
Chargement de la configuration par défaut...
Fichier de config fermé proprement.
Devise configurée : MAD

■ Le mot-clé 'with' (context manager) gère automatiquement la fermeture des fichiers —


c'est l'alternative moderne à finally pour les fichiers. Exemple : 'with open(chemin) as f:'
ferme f automatiquement, même en cas d'erreur.

© Formation Python Professionnelle Page 10


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

■ Exemple professionnel n°4 : Connexion à une base de données

■ connexion_bdd_securisee.py

# ■■■ Connexion BDD Sécurisée – Simulation SQLite


■■■■■■■■■■■■■■■■■■■
import sqlite3
def executer_requete(chemin_db, requete, params=()):
"""
Exécute une requête SQL de façon sécurisée.
Illustre : connexion → requête → résultat → déconnexion garantie.
"""
connexion = None
try:
connexion = [Link](chemin_db)
curseur = [Link]()
[Link](requete, params)
resultats = [Link]()
except [Link] as e:
print(f'■ Erreur SQL : {e}')
return None
except [Link] as e:
print(f'■ Erreur base de données : {e}')
return None
else:
# Code qui dépend du succès de la requête
print(f'■ Requête OK – {len(resultats)} enregistrement(s) trouvé(s).')
return resultats
finally:
# La connexion est TOUJOURS fermée – même si la requête plante
if connexion:
[Link]()
print(' Connexion BDD fermée.')
# ■■ Utilisation
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
employes = executer_requete(
'rh_techmaroc.db',
'SELECT nom, salaire FROM employes WHERE departement = ?',
('Ingénierie',)
)

© Formation Python Professionnelle Page 11


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

CHAPITRE 5

Lever ses Propres Exceptions : raise


raise · exceptions personnalisées · hiérarchie custom

Le mot-clé raise
Jusqu'ici, les exceptions étaient levées par Python quand quelque chose se passait mal. Mais vous
pouvez aussi les lever vous-même avec raise quand une valeur est techniquement correcte mais
incohérente dans le contexte métier.

■ syntaxe_raise.py

# Lever une exception standard


raise ValueError('Le salaire ne peut pas être négatif.')
# Lever une exception capturée et la re-propager
try:
resultat = operation_risquee()
except ValueError as e:
print(f'Log : {e}') # journaliser d'abord
raise # re-lever l'exception originale

Créer ses propres classes d'exception


Pour des projets professionnels, créez des exceptions personnalisées qui héritent de Exception.
Elles rendent les erreurs métier explicites, facilitent le débogage et permettent des blocs except
précis.

■ exceptions_personnalisees.py

# ■■■ Exceptions Personnalisées – Domaine RH


■■■■■■■■■■■■■■■■■■■■■■■■■
class ErreurRH(Exception):
"""Classe de base pour toutes les erreurs du module RH."""
pass
class SalaireInvalideError(ErreurRH):
"""Levée quand un salaire a une valeur métier incorrecte."""
def __init__(self, salaire, message=''):
[Link] = salaire
super().__init__(
message or f'Salaire invalide : {salaire} MAD'
)
class DepartementIntrouvableError(ErreurRH):
"""Levée quand un département n'existe pas dans le système."""

© Formation Python Professionnelle Page 12


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

pass
class QuotaCongesDepasseError(ErreurRH):
"""Levée quand un employé dépasse son quota de congés."""
def __init__(self, nom, jours_demandes, solde):
[Link] = nom
self.jours_demandes = jours_demandes
[Link] = solde
super().__init__(
f"{nom} demande {jours_demandes}j mais solde = {solde}j"
)

■ Exemple professionnel n°5 : Système de gestion des congés

■ gestion_conges.py

# ■■■ Gestion des Congés – RH TechMaroc


■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
DEPARTEMENTS_VALIDES = ['Ingénierie', 'Finance', 'RH', 'Commercial']
def valider_employe(nom, salaire, departement):
"""Valide les données d'un employé. Lève des exceptions si invalide."""
if not nom or not isinstance(nom, str):
raise ValueError('Le nom doit être une chaîne non vide.')
if salaire < 0:
raise SalaireInvalideError(salaire)
if salaire < 2772: # SMIG Maroc 2025
raise SalaireInvalideError(
salaire,
f'Salaire {salaire} MAD inférieur au SMIG (2772 MAD).'
)
if departement not in DEPARTEMENTS_VALIDES:
raise DepartementIntrouvableError(
f"Département '{departement}' inconnu. ",
)
def soumettre_conges(nom, solde, jours_demandes):
"""Soumet une demande de congé. Vérifie le quota disponible."""
if jours_demandes <= 0:
raise ValueError('Le nombre de jours doit être positif.')
if jours_demandes > solde:
raise QuotaCongesDepasseError(nom, jours_demandes, solde)
return True
# ■■ Tests des validations
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
try:

© Formation Python Professionnelle Page 13


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

valider_employe('Sara Alami', 4500, 'Ingénierie')


print('■ Employée Sara Alami validée.')
except ErreurRH as e:
print(f'■ Erreur RH : {e}')
try:
valider_employe('Karim', 1500, 'Ingénierie')
except SalaireInvalideError as e:
print(f'■ Salaire : {e}')
try:
valider_employe('Leila', 3200, 'Direction')
except DepartementIntrouvableError as e:
print(f'■ Département : {e}')
try:
soumettre_conges('Youssef', solde=5, jours_demandes=12)
except QuotaCongesDepasseError as e:
print(f'■ Congés : {e}')

■ Sortie console

■ Employée Sara Alami validée.


■ Salaire : Salaire 1500 MAD inférieur au SMIG (2772 MAD).
■ Département : Département 'Direction' inconnu.
■ Congés : Youssef demande 12j mais solde = 5j

© Formation Python Professionnelle Page 14


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

CHAPITRE 6

Bonnes Pratiques & Application QA / Selenium


EAFP · Logging · Tests robustes · NoSuchElementException

Les 4 règles d'or

Soyez spécifique
Évitez 'except:' ou 'except Exception:' vides qui attrapent tout. Attrapez
1 uniquement les exceptions que vous attendez réellement. Un except trop large
peut masquer de vrais bugs.

Gardez les blocs try courts


N'entourez que les 1-3 lignes réellement susceptibles de lever une exception. Un
2 try trop long rend difficile de savoir quelle ligne a causé l'erreur et complique le
débogage.

Principe EAFP : demandez le pardon, pas la permission


3 En Python, il est plus idiomatique d'essayer l'opération et de gérer l'erreur plutôt
que de vérifier d'abord si elle est possible. C'est plus performant et plus lisible.

Ne masquez jamais les erreurs silencieusement


Un except avec juste 'pass' est presque toujours une mauvaise idée. Au
4 minimum, journalisez l'erreur. Une erreur ignorée silencieusement devient un bug
impossible à diagnostiquer en production.

EAFP vs LBYL : comparaison concrète

■ eafp_vs_lbyl.py

# ■■■ Accès à un dictionnaire employé


■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
employe = {'nom': 'Sara', 'salaire': 4500}
# ■ LBYL (Look Before You Leap) – Moins Pythonique
if 'email' in employe:
if employe['email']: # 2 vérifications
print(employe['email'])
else:
print('Email vide.')
else:
print('Clé email absente.')

© Formation Python Professionnelle Page 15


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

# ■ EAFP (Easier to Ask Forgiveness than Permission) – Pythonique


try:
print(employe['email']) # 1 seul essai direct
except KeyError:
print('Email non renseigné.')

■ Application QA / Selenium – Tester une déconnexion


En Quality Assurance, la gestion des exceptions est cruciale. Un test qui plante sans message clair est inutile.
Un test bien géré donne un diagnostic précis du problème.

■ test_deconnexion_selenium.py

# ■■■ Test de Déconnexion – Framework QA avec Selenium


■■■■■■■■■■■■■■
from [Link] import (
NoSuchElementException,
TimeoutException,
WebDriverException
)
# ■■ Exception personnalisée pour le contexte QA
■■■■■■■■■■■■■■■■■■■■■
class UtilisateurNonConnecteError(Exception):
"""Levée si l'utilisateur n'est pas connecté lors d'un test."""
pass
class EchecTest(Exception):
"""Levée pour marquer explicitement l'échec d'un cas de test."""
pass
def deconnecter_utilisateur(driver):
"""
Scénario de test : cliquer sur le bouton Déconnexion.
Gère les cas : succès, bouton absent, timeout, erreur driver.
"""
try:
# Chercher le bouton de déconnexion (risque : absent)
bouton = driver.find_element_by_id('btn-logout')
[Link]()
except NoSuchElementException:
# Bouton absent → l'utilisateur n'est probablement pas connecté
raise UtilisateurNonConnecteError(
'Bouton #btn-logout introuvable – session non ouverte ?'
)
except TimeoutException:
raise EchecTest('Timeout : la page n\'a pas répondu en temps voulu.')

© Formation Python Professionnelle Page 16


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

except WebDriverException as e:
raise EchecTest(f'Erreur WebDriver inattendue : {e}')
else:
# S'exécute uniquement si le clic a réussi
print('■ TEST PASSÉ : déconnexion réussie.')
finally:
# TOUJOURS : screenshot pour le rapport de bug
driver.save_screenshot('rapport_deconnexion.png')
print(' Screenshot sauvegardé → rapport_deconnexion.png')
# ■■ Exécution du test
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
try:
deconnecter_utilisateur(mon_driver)
except UtilisateurNonConnecteError as e:
print(f'■ PRÉCONDITION ÉCHOUÉE : {e}')
print(' → Vérifiez que le test de connexion précédent a réussi.')
except EchecTest as e:
print(f'■ TEST ÉCHOUÉ : {e}')

■ En QA professionnel, les exceptions personnalisées (EchecTest,


UtilisateurNonConnecteError) permettent aux frameworks de test (pytest, unittest) de
distinguer un ÉCHEC PRÉVU d'une ERREUR INATTENDUE et de générer des rapports
HTML exploitables par toute l'équipe.

© Formation Python Professionnelle Page 17


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

CHAPITRE 7

Projet Professionnel Complet


Système de traitement de commandes e-commerce – Pipeline 100% robuste

Ce projet combine tous les concepts du module : exceptions intégrées, try/except/else/finally,


exceptions personnalisées, raise, bonnes pratiques EAFP, et logging. Il simule le pipeline complet de
traitement d'une commande e-commerce avec gestion d'erreurs à chaque étape.

■ Architecture : chaque fonction a son propre bloc try/except ciblé. Les erreurs métier
utilisent des exceptions personnalisées. Le pipeline principal orchestre tout avec un finally de
nettoyage.

■ pipeline_commande_robuste.py

# ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
■■■■■■■■■■■■
# PIPELINE DE COMMANDE – TECHSHOP ONLINE
# Gestion complète des exceptions à chaque étape
# ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
■■■■■■■■■■■■
import json, datetime
# ■■ EXCEPTIONS PERSONNALISÉES DU DOMAINE
■■■■■■■■■■■■■■■■■■■■■■■■■
class ErreurCommande(Exception):
"""Classe de base pour toutes les erreurs du pipeline commande."""
pass
class StockInsuffisantError(ErreurCommande):
def __init__(self, produit, demande, dispo):
super().__init__(f'{produit} : demande={demande}, dispo={dispo}')
class PaiementRefuseError(ErreurCommande):
def __init__(self, raison):
super().__init__(f'Paiement refusé : {raison}')
class AdresseLivraisonInvalideError(ErreurCommande):
pass
# ■■ DONNÉES SIMULÉES
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
CATALOGUE = {
'P001': {'nom': 'Laptop Pro X', 'prix': 8500, 'stock': 5},
'P002': {'nom': 'Souris Sans Fil', 'prix': 250, 'stock': 2},
'P003': {'nom': 'Clavier Méca.', 'prix': 450, 'stock': 0},
}

© Formation Python Professionnelle Page 18


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

# ■■ ÉTAPE 1 : Vérification du stock


■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
def verifier_stock(produit_id, quantite):
"""Vérifie la disponibilité. Lève StockInsuffisantError si KO."""
try:
produit = CATALOGUE[produit_id] # risque : KeyError
except KeyError:
raise ErreurCommande(f'Produit inconnu : {produit_id}')
if produit['stock'] < quantite:
raise StockInsuffisantError(
produit['nom'], quantite, produit['stock']
)
return produit
# ■■ ÉTAPE 2 : Calcul du montant
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
def calculer_total(produit, quantite, code_promo=None):
"""Calcule le total TTC avec remise éventuelle."""
PROMOS = {'SAVE10': 0.10, 'SAVE20': 0.20, 'VIP30': 0.30}
sous_total = produit['prix'] * quantite
remise = 0
if code_promo:
try:
taux = PROMOS[code_promo] # risque : KeyError
remise = sous_total * taux
print(f' Code promo {code_promo} appliqué : -{remise:.2f} MAD')
except KeyError:
print(f' ■ Code promo invalide {code_promo!r} – ignoré.')
ht = sous_total - remise
tva = ht * 0.20
total = round(ht + tva, 2)
return sous_total, remise, total
# ■■ ÉTAPE 3 : Validation paiement
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
def valider_paiement(montant, carte):
"""Simule la validation d'un paiement. Lève PaiementRefuseError si
KO."""
try:
numero = [Link]('numero', '')
solde = float([Link]('solde', 0))
except (KeyError, ValueError) as e:
raise ErreurCommande(f'Carte bancaire invalide : {e}')
if len(str(numero)) != 16:
raise PaiementRefuseError('numéro de carte invalide (16 chiffres
requis)')

© Formation Python Professionnelle Page 19


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

if solde < montant:


raise PaiementRefuseError(
f'solde insuffisant ({solde:.2f} MAD < {montant:.2f} MAD)'
)
return True
# ■■ ÉTAPE 4 : Validation adresse
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
def valider_adresse(adresse):
"""Valide l'adresse de livraison."""
champs_requis = ['rue', 'ville', 'code_postal']
manquants = [c for c in champs_requis if not [Link](c)]
if manquants:
raise AdresseLivraisonInvalideError(
f"Champs manquants : {', '.join(manquants)}"
)
return True
# ■■ PIPELINE PRINCIPAL
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
def passer_commande(client, produit_id, quantite, carte, adresse,
promo=None):
"""
Pipeline complet : stock → calcul → paiement → adresse → confirmation.
Gère toutes les erreurs possibles à chaque étape.
"""
commande_id = None
try:
print(f'\n=== COMMANDE – {client} ===')
# Étape 1 : stock
produit = verifier_stock(produit_id, quantite)
print(f'■ Stock OK : {produit["nom"]} x{quantite}')
# Étape 2 : calcul
sous_total, remise, total = calculer_total(produit, quantite, promo)
print(f'■ Total TTC : {total:.2f} MAD')
# Étape 3 : paiement
valider_paiement(total, carte)
print('■ Paiement autorisé.')
# Étape 4 : adresse
valider_adresse(adresse)
print('■ Adresse de livraison validée.')
except StockInsuffisantError as e:
print(f'■ STOCK INSUFFISANT : {e}')
return None
except PaiementRefuseError as e:

© Formation Python Professionnelle Page 20


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

print(f'■ PAIEMENT REFUSÉ : {e}')


return None
except AdresseLivraisonInvalideError as e:
print(f'■ ADRESSE INVALIDE : {e}')
return None
except ErreurCommande as e:
print(f'■ ERREUR COMMANDE : {e}')
return None
except Exception as e:
# Filet de sécurité pour les erreurs imprévues (logger en prod)
print(f'■ ERREUR INATTENDUE : {type(e).__name__}: {e}')
return None
else:
# Tout a réussi : confirmer la commande
import random
commande_id = f'CMD-{[Link](10000,99999)}'
CATALOGUE[produit_id]['stock'] -= quantite # MAJ stock
print(f'■ COMMANDE CONFIRMÉE : {commande_id}')
return commande_id
finally:
# TOUJOURS : journaliser la tentative
horodatage = [Link]().strftime('%H:%M:%S')
statut = 'OK' if commande_id else 'ECHEC'
print(f' [LOG {horodatage}] {client} | {produit_id} | {statut}')
# ■■ TESTS DU PIPELINE
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
carte_ok = {'numero': '1234567890123456', 'solde': 15000}
carte_ko = {'numero': '1234567890123456', 'solde': 100}
adresse_ok = {'rue': '12 Bd Zerktouni', 'ville': 'Casablanca',
'code_postal': '20000'}
adresse_ko = {'rue': '12 Bd Zerktouni'}
passer_commande('Karim Benali', 'P001', 2, carte_ok, adresse_ok,
'SAVE10')
passer_commande('Sara Alami', 'P003', 1, carte_ok, adresse_ok)
passer_commande('Leila Haddad', 'P001', 1, carte_ko, adresse_ok)
passer_commande('Youssef Idris', 'P002', 1, carte_ok, adresse_ko)

© Formation Python Professionnelle Page 21


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

Récapitulatif Général

Élément Syntaxe Rôle Quand l'utiliser

try try: Bloc de code risqué à surveillerToujours en premier

except (simple) except ValueError: Gère UN type d'exception précis


Traitement spécifique par type

except (tuple) except (V, Z) as e: Gère plusieurs exceptions identiquement


Même message d'erreur pour N exceptions

except (as e) except Exception as e: Capture l'objet exception (message)


Pour logger str(e) ou repr(e)

else else: Code exécuté si AUCUNE exception


Traitement du résultat du try

finally finally: Code exécuté TOUJOURS Fermer fichier, BDD, session, screenshot

raise raise MonErreur('msg') Lever une exception manuellement


Règles métier, validation des données

Exception custom class MonErr(Exception):


Exception
pass avec nom métier explicite
Projets pro, bibliothèques, frameworks QA

EAFP try/except (vs if/else) Essayer d'abord, gérer si erreur


Accès dict, fichiers, conversions de types

Les 5 erreurs classiques à éviter absolument

■ except: seul (sans type)


Problème : Attrape TOUT y compris KeyboardInterrupt. Cache des bugs graves.

■ Solution : Toujours spécifier le type : except ValueError:

■ except Exception as e: pass


Problème : L'erreur est avalée silencieusement. Impossible à déboguer en production.

■ Solution : Au minimum : print(e) ou [Link](e)

■ Bloc try trop grand


Problème : Entourer 20 lignes dans un try rend impossible de savoir laquelle plante.

■ Solution : try ne couvre que 1-3 lignes réellement risquées

■ Oublier finally pour les ressources


Problème : Un fichier ou une connexion BDD non fermé(e) = fuite mémoire en production.

■ Solution : Utilisez finally ou le context manager 'with'

■ raise sans message explicite


Problème : raise Exception() sans message ne dit rien à celui qui debug le code.

■ Solution : raise ValueError('Salaire négatif reçu : -500')

© Formation Python Professionnelle Page 22


Formation Python · Gestion des Exceptions try · except · else · finally · raise · exceptions personnalisées

■ Prochaines étapes recommandées

■ Module logging : journaliser les erreurs dans des fichiers de log


■ Context managers (with) : gestion automatique des ressources
■ pytest : écrire et automatiser des tests unitaires en Python
■ Selenium WebDriver : automatisation de tests d'interface web
■ Décorateurs : créer des gestionnaires d'exception réutilisables

© Formation Python Professionnelle Page 23

Vous aimerez peut-être aussi