Gestion des exceptions et journalisation

PythonPythonBeginner
Pratiquer maintenant

This tutorial is from open-source community. Access the source code

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

Dans ce laboratoire (lab), vous apprendrez à implémenter la gestion des exceptions en Python. Vous comprendrez comment utiliser le module de journalisation (logging module) pour une meilleure signalisation des erreurs, ce qui est crucial pour le débogage et la maintenance de votre code.

Vous allez également pratiquer la modification du fichier reader.py pour gérer les erreurs de manière élégante plutôt que de faire planter le programme. Cette expérience pratique améliorera votre capacité à écrire des programmes Python robustes.

Comprendre les exceptions en Python

Dans cette étape, nous allons apprendre à propos des exceptions en Python. Les exceptions sont un concept important en programmation. Elles nous aident à gérer les situations inattendues qui peuvent se produire lors de l'exécution d'un programme. Nous allons également comprendre pourquoi le code actuel plante lorsqu'il tente de traiter des données invalides. Comprendre cela vous aidera à écrire des programmes Python plus robustes et fiables.

Qu'est-ce qu'une exception ?

En Python, les exceptions sont des événements qui se produisent lors de l'exécution d'un programme et qui perturbent le flux normal des instructions. Imaginez - le comme un obstacle sur une autoroute. Lorsque tout se passe bien, votre programme suit un chemin prédéfini, tout comme une voiture sur une route dégagée. Mais lorsqu'une erreur se produit, Python crée un objet exception. Cet objet est comme un rapport qui contient des informations sur ce qui a mal tourné, comme le type d'erreur et où elle s'est produite dans le code.

Si ces exceptions ne sont pas correctement gérées, elles font planter le programme. Lorsqu'un plantage se produit, Python affiche un message de trace (traceback message). Ce message est comme une carte qui vous montre l'emplacement exact dans le code où l'erreur s'est produite. C'est très utile pour le débogage.

Examiner le code actuel

Regardons d'abord la structure du fichier reader.py. Ce fichier contient des fonctions utilisées pour lire et convertir des données CSV. Pour ouvrir le fichier dans l'éditeur, nous devons nous rendre dans le bon répertoire. Nous allons utiliser la commande cd dans le terminal.

cd /home/labex/project

Maintenant que nous sommes dans le bon répertoire, regardons le contenu de reader.py. Ce fichier a plusieurs fonctions importantes :

  1. convert_csv() : Cette fonction prend des lignes de données et utilise une fonction de conversion fournie pour les convertir. C'est comme une machine qui prend des matières premières (les lignes de données) et les transforme en une autre forme selon une recette spécifique (la fonction de conversion).
  2. csv_as_dicts() : Cette fonction lit des données CSV et les transforme en une liste de dictionnaires. Elle effectue également une conversion de type, ce qui signifie qu'elle s'assure que chaque élément de données dans le dictionnaire est du bon type, comme une chaîne de caractères, un entier ou un nombre à virgule flottante.
  3. read_csv_as_dicts() : C'est une fonction d'enrobage (wrapper function). C'est comme un gestionnaire qui appelle la fonction csv_as_dicts() pour faire le travail.

Mettre en évidence le problème

Voyons ce qui se passe lorsque le code tente de traiter des données invalides. Nous allons ouvrir un interpréteur Python, qui est comme un terrain de jeu où nous pouvons tester notre code Python de manière interactive. Pour ouvrir l'interpréteur Python, nous allons utiliser la commande suivante dans le terminal :

python3

Une fois l'interpréteur Python ouvert, nous allons essayer de lire le fichier missing.csv. Ce fichier contient des données manquantes ou invalides. Nous allons utiliser la fonction read_csv_as_dicts() du fichier reader.py pour lire les données.

from reader import read_csv_as_dicts
port = read_csv_as_dicts('missing.csv', types=[str, int, float])

Lorsque vous exécutez ce code, vous devriez voir un message d'erreur comme celui - ci :

Traceback (most recent call last):
  ...
ValueError: invalid literal for int() with base 10: ''

Cette erreur se produit parce que le code tente de convertir une chaîne de caractères vide en entier. Une chaîne de caractères vide ne représente pas un entier valide, donc Python ne peut pas effectuer la conversion. La fonction plante dès la première erreur qu'elle rencontre, et elle arrête le traitement du reste des données valides dans le fichier.

Pour quitter l'interpréteur Python, tapez la commande suivante :

exit()

Comprendre le flux des erreurs

L'erreur se produit dans la fonction convert_csv(), plus précisément à la ligne suivante :

return list(map(lambda row: converter(headers, row), rows))

La fonction map() applique la fonction converter à chaque ligne de la liste rows. La fonction converter tente d'appliquer les types (str, int, float) à chaque ligne. Mais lorsqu'elle rencontre une ligne avec des données manquantes, elle échoue. La fonction map() n'a pas de moyen intégré pour gérer les exceptions. Donc, lorsqu'une exception se produit, tout le processus plante.

Dans l'étape suivante, vous allez modifier le code pour gérer ces exceptions de manière élégante. Cela signifie que au lieu de planter, le programme sera capable de gérer les erreurs et de continuer à traiter le reste des données.

Implémentation de la gestion des exceptions

Dans cette étape, nous allons nous concentrer sur la mise en œuvre de la gestion des exceptions pour rendre votre code plus robuste. Lorsqu'un programme rencontre des données erronées, il a souvent tendance à planter. Cependant, nous pouvons utiliser une technique appelée gestion des exceptions pour gérer ces problèmes de manière élégante. Vous allez modifier le fichier reader.py pour implémenter cela. La gestion des exceptions permet à votre programme de continuer à fonctionner même lorsqu'il rencontre des données inattendues, au lieu de s'arrêter brusquement.

Comprendre les blocs try - except

Python offre un moyen puissant de gérer les exceptions en utilisant des blocs try - except. Analysons comment ils fonctionnent.

try:
    ## Code qui peut provoquer une exception
    result = risky_operation()
except SomeExceptionType as e:
    ## Code qui s'exécute si l'exception se produit
    handle_exception(e)

Dans le bloc try, vous placez le code qui peut lever une exception. Une exception est une erreur qui se produit lors de l'exécution d'un programme. Par exemple, si vous essayez de diviser un nombre par zéro, Python lèvera une exception ZeroDivisionError. Lorsqu'une exception se produit dans le bloc try, Python arrête l'exécution du code dans le bloc try et saute au bloc except correspondant. Le bloc except contient le code qui gérera l'exception. SomeExceptionType est le type d'exception que vous souhaitez capturer. Vous pouvez capturer des types d'exceptions spécifiques ou utiliser une exception générale Exception pour capturer tous les types d'exceptions. La partie as e vous permet d'accéder à l'objet exception, qui contient des informations sur l'erreur.

Modification du code

Maintenant, appliquons ce que nous avons appris sur les blocs try - except à la fonction convert_csv(). Ouvrez le fichier reader.py dans votre éditeur.

  1. Remplacez la fonction convert_csv() actuelle par le code suivant :
def convert_csv(rows, converter, header=True):
    """
    Convert a sequence of rows to an output sequence according to a conversion function.
    """
    if header:
        headers = next(rows)
    else:
        headers = []

    result = []
    for row_idx, row in enumerate(rows, start=1):
        try:
            ## Try to convert the row
            result.append(converter(headers, row))
        except Exception as e:
            ## Print a warning message for bad rows
            print(f"Row {row_idx}: Bad row: {row}")
            continue

    return result

Dans cette nouvelle implémentation :

  • Nous utilisons une boucle for au lieu de map() pour traiter chaque ligne. Cela nous donne plus de contrôle sur le traitement de chaque ligne.
  • Nous enveloppons le code de conversion dans un bloc try - except. Cela signifie que si une exception se produit lors de la conversion d'une ligne, le programme ne plantera pas. Au lieu de cela, il saute au bloc except.
  • Dans le bloc except, nous affichons un message d'erreur pour les lignes invalides. Cela nous aide à identifier les lignes qui présentent des problèmes.
  • Après avoir affiché le message d'erreur, nous utilisons l'instruction continue pour sauter la ligne actuelle et continuer le traitement des lignes restantes.

Enregistrez le fichier après avoir apporté ces modifications.

Test de vos modifications

Testons votre code modifié avec le fichier missing.csv. Tout d'abord, ouvrez l'interpréteur Python en exécutant la commande suivante dans votre terminal :

python3

Une fois que vous êtes dans l'interpréteur Python, exécutez le code suivant :

from reader import read_csv_as_dicts
port = read_csv_as_dicts('missing.csv', types=[str, int, float])
print(f"Number of valid rows processed: {len(port)}")

Lorsque vous exécutez ce code, vous devriez voir des messages d'erreur pour chaque ligne problématique. Mais le programme continuera le traitement et retournera les lignes valides. Voici un exemple de ce que vous pourriez voir :

Row 4: Bad row: ['C', '', '53.08']
Row 7: Bad row: ['DIS', '50', 'N/A']
Row 8: Bad row: ['GE', '', '37.23']
Row 13: Bad row: ['INTC', '', '21.84']
Row 17: Bad row: ['MCD', '', '51.11']
Row 19: Bad row: ['MO', '', '70.09']
Row 22: Bad row: ['PFE', '', '26.40']
Row 26: Bad row: ['VZ', '', '42.92']
Number of valid rows processed: 20

Vérifions également que le programme fonctionne correctement avec des données valides. Exécutez le code suivant dans l'interpréteur Python :

valid_port = read_csv_as_dicts('valid.csv', types=[str, int, float])
print(f"Number of valid rows processed: {len(valid_port)}")

Vous devriez constater que toutes les lignes sont traitées sans erreur. Voici un exemple de sortie :

Number of valid rows processed: 17

Pour quitter l'interpréteur Python, exécutez la commande suivante :

exit()

Maintenant, votre code est plus robuste. Il peut gérer les données invalides de manière élégante en sautant les lignes erronées au lieu de planter. Cela rend votre programme plus fiable et plus convivial.

✨ Vérifier la solution et pratiquer

Implémentation de la journalisation (logging)

Dans cette étape, nous allons améliorer votre code. Au lieu d'utiliser de simples messages d'impression (print), nous allons utiliser le module logging de Python pour une journalisation appropriée. La journalisation est un excellent moyen de suivre ce que fait votre programme, notamment lorsqu'il s'agit de gérer les erreurs et de comprendre le flux de votre code.

Comprendre le module de journalisation

Le module logging de Python nous offre un moyen flexible d'envoyer des messages de journalisation depuis nos applications. Il est beaucoup plus puissant que l'utilisation simple d'instructions print. Voici ce qu'il peut faire :

  1. Différents niveaux de journalisation (DEBUG, INFO, WARNING, ERROR, CRITICAL) : Ces niveaux nous aident à catégoriser l'importance des messages. Par exemple, DEBUG est pour des informations détaillées utiles pendant le développement, tandis que CRITICAL est pour des erreurs graves qui pourraient arrêter le programme.
  2. Format de sortie configurable : Nous pouvons décider à quoi ressembleront les messages de journalisation, comme ajouter des horodatages ou d'autres informations utiles.
  3. Les messages peuvent être dirigés vers différentes sorties (console, fichiers, etc.) : Nous pouvons choisir d'afficher les messages de journalisation sur la console, de les enregistrer dans un fichier ou même de les envoyer à un serveur distant.
  4. Filtrage des journaux en fonction de la gravité : Nous pouvons contrôler quels messages nous voyons en fonction de leur niveau de journalisation.

Ajout de la journalisation au fichier reader.py

Maintenant, modifions votre code pour utiliser le module de journalisation. Ouvrez le fichier reader.py.

Tout d'abord, nous devons importer le module logging et configurer un enregistreur (logger) pour ce module. Ajoutez le code suivant en haut du fichier :

import logging

## Set up a logger for this module
logger = logging.getLogger(__name__)

L'instruction import logging importe le module logging afin que nous puissions utiliser ses fonctions. logging.getLogger(__name__) crée un enregistreur pour ce module spécifique. L'utilisation de __name__ garantit que l'enregistreur a un nom unique lié au module.

Ensuite, nous allons modifier la fonction convert_csv() pour utiliser la journalisation au lieu d'instructions print. Voici le code mis à jour :

def convert_csv(rows, converter, header=True):
    """
    Convert a sequence of rows to an output sequence according to a conversion function.
    """
    if header:
        headers = next(rows)
    else:
        headers = []

    result = []
    for row_idx, row in enumerate(rows, start=1):
        try:
            ## Try to convert the row
            result.append(converter(headers, row))
        except Exception as e:
            ## Log a warning message for bad rows
            logger.warning(f"Row {row_idx}: Bad row: {row}")
            ## Log the reason at debug level
            logger.debug(f"Row {row_idx}: Reason: {str(e)}")
            continue

    return result

Les principales modifications sont les suivantes :

  • Nous avons remplacé print() par logger.warning() pour le message d'erreur. De cette façon, le message est enregistré avec le niveau d'avertissement approprié, et nous pouvons contrôler sa visibilité plus tard.
  • Nous avons ajouté un nouveau message logger.debug() avec des détails sur l'exception. Cela nous donne plus d'informations sur ce qui a mal tourné, mais il n'est affiché que si le niveau de journalisation est défini sur DEBUG ou inférieur.
  • str(e) convertit l'exception en chaîne de caractères, afin que nous puissions afficher la raison de l'erreur dans le message de journalisation.

Après avoir apporté ces modifications, enregistrez le fichier.

Test de la journalisation

Testons votre code avec la journalisation activée. Ouvrez l'interpréteur Python en exécutant la commande suivante dans votre terminal :

python3

Une fois que vous êtes dans l'interpréteur Python, exécutez le code suivant :

import logging
import reader

## Configure logging level to see all messages
logging.basicConfig(level=logging.DEBUG)

port = reader.read_csv_as_dicts('missing.csv', types=[str, int, float])
print(f"Number of valid rows processed: {len(port)}")

Ici, nous importons d'abord le module logging et notre module reader. Ensuite, nous définissons le niveau de journalisation sur DEBUG en utilisant logging.basicConfig(level=logging.DEBUG). Cela signifie que nous verrons tous les messages de journalisation, y compris DEBUG, INFO, WARNING, ERROR et CRITICAL. Nous appelons ensuite la fonction read_csv_as_dicts du module reader et affichons le nombre de lignes valides traitées.

Vous devriez voir une sortie comme celle-ci :

WARNING:reader:Row 4: Bad row: ['C', '', '53.08']
DEBUG:reader:Row 4: Reason: invalid literal for int() with base 10: ''
WARNING:reader:Row 7: Bad row: ['DIS', '50', 'N/A']
DEBUG:reader:Row 7: Reason: could not convert string to float: 'N/A'
...
Number of valid rows processed: 20

Notez que le module de journalisation ajoute un préfixe à chaque message, indiquant le niveau de journalisation (WARNING/DEBUG) et le nom du module.

Maintenant, voyons ce qui se passe si nous changeons le niveau de journalisation pour n'afficher que les avertissements. Exécutez le code suivant dans l'interpréteur Python :

## Reset the logging configuration
import logging
logging.basicConfig(level=logging.WARNING)

port = reader.read_csv_as_dicts('missing.csv', types=[str, int, float])

Cette fois, nous définissons le niveau de journalisation sur WARNING en utilisant logging.basicConfig(level=logging.WARNING). Maintenant, vous ne verrez que les messages WARNING, et les messages DEBUG seront masqués :

WARNING:reader:Row 4: Bad row: ['C', '', '53.08']
WARNING:reader:Row 7: Bad row: ['DIS', '50', 'N/A']
...

Cela montre l'avantage d'utiliser différents niveaux de journalisation. Nous pouvons contrôler le niveau de détail affiché dans les journaux sans modifier notre code.

Pour quitter l'interpréteur Python, exécutez la commande suivante :

exit()

Félicitations ! Vous avez maintenant implémenté une gestion d'exceptions et une journalisation appropriées dans votre programme Python. Cela rend votre code plus fiable et vous fournit de meilleures informations en cas d'erreur.

Résumé

Dans ce laboratoire (lab), vous avez appris plusieurs concepts clés concernant la gestion des exceptions et la journalisation (logging) en Python. Tout d'abord, vous avez compris comment les exceptions se produisent lors du traitement des données et avez implémenté des blocs try - except pour les gérer de manière élégante. Vous avez également modifié le code pour continuer à traiter les données valides en cas d'erreur.

Deuxièmement, vous avez appris à connaître le module de journalisation de Python et ses avantages par rapport aux instructions print. Vous avez implémenté différents niveaux de journalisation tels que WARNING et DEBUG et avez vu comment configurer la journalisation pour différents niveaux de détail. Ces compétences sont essentielles pour écrire des applications Python robustes, notamment lorsqu'il s'agit de traiter des données externes sujettes à des erreurs, de construire des applications autonomes ou de développer des systèmes qui nécessitent des informations de diagnostic. Vous pouvez maintenant appliquer ces techniques à vos projets Python pour une meilleure fiabilité et maintenabilité.