Bonsoir,
Version courte : une fonction A appelle une fonction B. Si celle-ci déclenche une erreur, une partie des informations de débogage ne sont accessibles que depuis A. Comment remonter de manière propre et lisible l'erreur ? De manière brutale et peu lisible, je sais faire, voir ci-après.
Dans le cadre d'un petit projet en python, je me retrouve à avoir l'architecture suivante :
- quelques class ;
- une fonction qui lit différents fichiers json selon des paramètres (sans rien en faire, seulement lister / récupérer une chaine dans les fichiers) ;
- une fonction qui décode le json et crée plusieurs variables, instances des class.
Si je fais un exemple minimal équivalent, ça donne :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Objet: def __init__(self, param1, param2): self.param = float(param1) + float(param2) def read_file(filename): with open(filename) as file_data: return parser(file_data.read()) def parser(data): data = data.split("\n") dictionary = {} # pour chaque ligne non vide, récupère un nom et une valeur associée. for el in data: if el != "": el = el.split(" ") dictionary[el[0]] = el[1] return Objet(**dictionary) if __name__ == "__main__": to_read = "test" o = read_file(to_read) print(o.param) |
avec l'entrée qui va bien,
1 2 | param1 1 param2 2 |
Ça marche, ça affiche 3.0.
Le truc, c'est que le fichier json peut être mal écris, et dans ce cas, l'erreur retournée est illisible, on ne sait pas ce qui est faux dans le json, ni même dans quel fichier ! Par exemple, en enlevant la seconde ligne du fichier d'entrée,
1 2 3 4 5 6 7 8 9 | Traceback (most recent call last): File "pyf.py", line 20, in <module> o = read_file(to_read) File "pyf.py", line 7, in read_file return parser(file_data.read()) File "pyf.py", line 16, in parser return Objet(**dictionary) TypeError: __init__() missing 1 required positional argument: 'param2' [1] 29055 exit 1 python3 pyf.py |
J'ai modifié le code pour remonter l'erreur assez bêtement (dans le cas d'un manque d'argument, un TypeError
). Cependant, il faut vraiment remonter l'erreur, et non pas seulement la capturer, car parser ne sait pas quel est le nom du fichier qu'il traite.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | class Objet: def __init__(self, param1, param2): self.param = float(param1) + float(param2) def read_file(filename): with open(filename) as file_data: # Modif ici try: return parser(file_data.read()) except TypeError: raise TypeError("Error occured on file '{file}'.".format(file=filename)) def parser(data): data = data.split("\n") dictionary = {} for el in data: if el != "": el = el.split(" ") dictionary[el[0]] = el[1] # Modif ici aussi try: return Objet(**dictionary) except TypeError: raise TypeError("Error when parsing. Object can not be parsed with value {value}.".\ format(value=dictionary)) if __name__ == "__main__": to_read = "test" o = read_file(to_read) print(o.param) |
L'erreur devient,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | Traceback (most recent call last): File "pyf.py", line 20, in parser return Objet(**dictionary) TypeError: __init__() missing 1 required positional argument: 'param2' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "pyf.py", line 8, in read_file return parser(file_data.read()) File "pyf.py", line 23, in parser format(value=dictionary)) TypeError: Error when parsing. Object can not be parsed with value {'param1': '1'}. During handling of the above exception, another exception occurred: Traceback (most recent call last): File "pyf.py", line 27, in <module> o = read_file(to_read) File "pyf.py", line 10, in read_file raise TypeError("Error occured on file '{file}'.".format(file=filename)) TypeError: Error occured on file 'test'. [1] 29674 exit 1 python3 pyf.py |
On a accès à toutes les infos. Mais non seulement, c'est très peu lisible (3 messages d'erreur à lire pour retrouver toutes les infos), mais en plus, ça ne couvre qu'une toute petite partie des erreurs (le manque d’argument).
D'où ma question : comment faire ça proprement, plutôt qu'un bête « affiche un message et passe au voisin du dessus ».
En espérant avoir été clair et concis.