Usage#

Pile d’appel#

La pile d’appel (ou pile d’exécution ou call stack) mémorise les appels de fonctions. La premièrel ligne est le point d’entrée du programme. La suivante est la seconde fonction appelée. Si celle-ci en appelle une autre, une autre ligne est ajoutée et celle-ci demeure jusqu’à ce qu’elle est terminée son exécution. A chaque instant, la dernière ligne est la fonction en train de s’exécuter, les lignes précédentes définissent le chemin que l’ordinateur a suivi pour arriver jusque là.

<<<

import traceback
import sys


def foncA():
    print("foncA begin")
    foncB()
    print("foncA end")


def foncB():
    print("foncB begin")
    foncC()
    print("foncB end")


def foncC():
    print("foncC begin")
    try:
        raise Exception("erreur volontaire")
    except Exception:
        print("Erreur")
        print("\n".join(traceback.format_stack()))
    print("foncC end")


foncA()

>>>

    foncA begin
    foncB begin
    foncC begin
    Erreur
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/pyquickhelper/helpgen/process_sphinx_cmd.py", line 23, in <module>
        main()
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/pyquickhelper/helpgen/process_sphinx_cmd.py", line 19, in main
        run_sphinx_build(sys.argv)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/pyquickhelper/helpgen/process_sphinx_cmd.py", line 15, in run_sphinx_build
        build_main(argv=argv[1:])
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/sphinx/cmd/build.py", line 290, in main
        return build_main(argv)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/sphinx/cmd/build.py", line 276, in build_main
        app.build(args.force_all, filenames)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/sphinx/application.py", line 330, in build
        self.builder.build_update()
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/sphinx/builders/__init__.py", line 286, in build_update
        self.build(to_build,
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/sphinx/builders/__init__.py", line 300, in build
        updated_docnames = set(self.read())
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/sphinx/builders/__init__.py", line 407, in read
        self._read_serial(docnames)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/sphinx/builders/__init__.py", line 428, in _read_serial
        self.read_doc(docname)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/sphinx/builders/__init__.py", line 468, in read_doc
        doctree = read_doc(self.app, self.env, self.env.doc2path(docname))
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/sphinx/io.py", line 181, in read_doc
        pub.publish()
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/core.py", line 217, in publish
        self.document = self.reader.read(self.source, self.parser,
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/sphinx/io.py", line 101, in read
        self.parse()
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/readers/__init__.py", line 78, in parse
        self.parser.parse(self.input, document)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/sphinx/parsers.py", line 89, in parse
        self.statemachine.run(inputlines, document, inliner=self.inliner)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 170, in run
        results = StateMachineWS.run(self, input_lines, input_offset,
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/statemachine.py", line 239, in run
        context, next_state, result = self.check_line(
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/statemachine.py", line 451, in check_line
        return method(match, context, next_state)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 3008, in text
        self.section(title.lstrip(), source, style, lineno + 1, messages)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 327, in section
        self.new_subsection(title, lineno, messages)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 393, in new_subsection
        newabsoffset = self.nested_parse(
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 281, in nested_parse
        state_machine.run(block, input_offset, memo=self.memo,
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 196, in run
        results = StateMachineWS.run(self, input_lines, input_offset)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/statemachine.py", line 239, in run
        context, next_state, result = self.check_line(
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/statemachine.py", line 451, in check_line
        return method(match, context, next_state)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2769, in underline
        self.section(title, source, style, lineno - 1, messages)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 327, in section
        self.new_subsection(title, lineno, messages)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 393, in new_subsection
        newabsoffset = self.nested_parse(
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 281, in nested_parse
        state_machine.run(block, input_offset, memo=self.memo,
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 196, in run
        results = StateMachineWS.run(self, input_lines, input_offset)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/statemachine.py", line 239, in run
        context, next_state, result = self.check_line(
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/statemachine.py", line 451, in check_line
        return method(match, context, next_state)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2342, in explicit_markup
        nodelist, blank_finish = self.explicit_construct(match)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2354, in explicit_construct
        return method(self, expmatch)
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2096, in directive
        return self.run_directive(
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/docutils/parsers/rst/states.py", line 2146, in run_directive
        result = directive_instance.run()
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/pyquickhelper/sphinxext/sphinx_runpython_extension.py", line 572, in run
        out, err, context = run_python_script(script, comment=comment, setsysvar=p['setsysvar'],
    
      File "somewhere/workspace/teachpyx/teachpyx_UT_39_std/_venv/lib/python3.9/site-packages/pyquickhelper/sphinxext/sphinx_runpython_extension.py", line 231, in run_python_script
        exec(obj, globs, loc)
    
      File "", line 26, in <module>
    
      File "", line 25, in run_python_script_140012400635136
    
      File "", line 8, in foncA
    
      File "", line 13, in foncB
    
      File "", line 22, in foncC
    
    foncC end
    foncB end
    foncA end

Récupération de la pile d’appel#

Le module traceback permet de récupérer la pile d’appels lorsqu’une exception survient.

<<<

def raise_exception():
    raise Exception("an error was raised")


try:
    insidefe()
except:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    print("".join(traceback.format_tb(exc_traceback)))

>>>

      File "", line 7, in run_python_script_140012400631360

Il est possible de récupérer la liste des appels de fonctions avec la fonction extract_tb. Cette information est précieuse pour écrire un test qui vérifie qu’une erreur s’est bien produite à un endroit particulier, de détecter les cas particuliers comme les boucles infinies ou d’améliorer un message d’erreur en cas de besoin (lire How do I write Flask’s excellent debug log message to a file in production?).

Message d’erreur plus explicite#

Lorsqu’une erreur se produit dans une librairie de Python, le message ne mentionne aucune information à propos du code qui l’a provoquée.

<<<

import math
ensemble = [1, 0, 2]
s = 0
for e in ensemble:
    s += math.log(e)

>>>

    
    [runpythonerror]
    
    Traceback (most recent call last):
        exec(obj, globs, loc)
      File "", line 8, in <module>
      File "", line 7, in run_python_script_140012399864448
    ValueError: math domain error

Typiquement dans ce cas précis, on ne sait pas quel est l’indice de l’élément qui a provoqué l’erreur. On utilise alors un mécanisme qui permet d’ajouter une erreur sans perdre les informations l’exception original

<<<

import math
ensemble = [1, 0, 2]
s = 0
for i, e in enumerate(ensemble):
    try:
        s += math.log(e)
    except Exception as exc:
        raise Exception("Issue with element {0}".format(i)) from exc

>>>

    
    [runpythonerror]
    
    Traceback (most recent call last):
      File "", line 8, in run_python_script_140012400750848
    ValueError: math domain error
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
        exec(obj, globs, loc)
      File "", line 11, in <module>
      File "", line 10, in run_python_script_140012400750848
    Exception: Issue with element 1

La dernière partie de la dernière ligne est importante : from exc. langage garde ainsi la trace de la première exception.

Conventions#

Erreur ou code d’erreur#

A faire: terminer la section Erreur ou code d’erreur

parler aussi de coûts d’une exception, libération des ressources