Le style de programmation par exceptions

Exception
Les exceptions permettent de récupérer des situations où le calcul ne peut pas se poursuivre. Dans ces cas, un récupérateur d’exceptions permet de continuer le calcul sachant qu’une des branches a échoué.

Exemple d”exception

>>> assert 2 == 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

La règle du samouraï

Important

règle du Samouraï : une fonction doit renvoyer le résultat escompté ou bien lever une exception

>>> def function_raise(x):
...   if not type(x) == int:
...     raise TypeError("Pas le bon type")
...   else:
...     return x + 1
...
>>> try:
...   e = function_raise("une string")
... except TypeError, e:
...   print e
...
Pas le bon type
>>
  • la fonction doit renvoyer des valeurs du même type (ou bien None)
  • la fonction doit lever une exception appropriée

utiliser la pile d’appel pour débugger

Utiliser la pile d’appels, elle se lit de bas en haut. Il est possible de provoquer cette pile d’appels.

import traceback
traceback.print_exc()

À faire

s’exercer à lire une pile d’appels un peu complexe.

Traiter une exception avec

try:
    ...
except:
    import traceback
    traceback.print_exc()

else:
    bla bla
finally:
    bla bla

Attention

ne jamais briser la pile d’appels sans savoir précisément ce que l’on fait, et remonter une autre exception

Exemple :

>>> def function(x):
...   if x == 1: raise TypeError("pas le bon type")
...   else: return "ok"
...
>>> def function2(x):
...   try:
...     return "c'est " + function(x)
...   except TypeError, e:
...     raise AssertionError("on a une autre exception là")
...
>>> try:
...   function2(1)
... except Exception, e:
...   print e
...
on a une autre exception là
>>>

Le raise (ou le re-raise) est souvent utilisé pour lever une exception métier et cacher une exception système de bas niveau.

exemple:

try:
  add_s(dn, listdata)
except ldap.LDAPError, err:
  raise MyOwnError(msg + str(err))
except Exception:
  pass

À faire

dans quel cas entrons-nous dans le else ? dans le finally ?

À faire

créer des exceptions métier

exemple d’exceptions dans la vraie vie:

"user defined exceptions"


# Exceptions for an Option
class PropertiesOptionError(AttributeError):
    "attempt to access to an option with a property that is not allowed"
    def __init__(self, msg, proptype):
        self.proptype = proptype
        super(PropertiesOptionError, self).__init__(msg)


#____________________________________________________________
# Exceptions for a Config
class ConfigError(Exception):
    """attempt to change an option's owner without a value
    or in case of `_cfgimpl_descr` is None
    or if a calculation cannot be carried out"""
    pass


class ContextError(Exception):
    """context needed but not given
    """
    pass


class ConflictError(Exception):
    "duplicate options are present in a single config"
    pass


#____________________________________________________________
# miscellaneous exceptions
class RequirementError(Exception):
    """a recursive loop occurs in the requirements tree
    requires
    """
    pass


class SlaveError(Exception):
    "problem with a slave's value length"
    pass


class ConstError(TypeError):
    "no uniq value in _NameSpace"
    pass

Créer le plus possible ses propres exceptions spécifiques au programme La programmation par exception peut vite devenir un style de programmation très utilisé. C’est assez agréable mais le réserver pour la gestion d’une situation particulière, que ça reste une intervention pour les cas non gérés par le programme (en général).

Les exceptions imbriquées

>>> try:
...   ma_func()
... except TypeError, e:
...   do_something()
... except Exception, e:
...   do_somethin_else()
...

Exemple de programme utilisant massivement la programmation par exceptions : tiramisu

La hiérarchie des exceptions

Extrait de la documentation officielle:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StandardError
      |    +-- BufferError
      |    +-- ArithmeticError
      |    |    +-- FloatingPointError
      |    |    +-- OverflowError
      |    |    +-- ZeroDivisionError
      |    +-- AssertionError
      |    +-- AttributeError
      |    +-- EnvironmentError
      |    |    +-- IOError
      |    |    +-- OSError
      |    |         +-- WindowsError (Windows)
      |    |         +-- VMSError (VMS)
      |    +-- EOFError
      |    +-- ImportError
      |    +-- LookupError
      |    |    +-- IndexError
      |    |    +-- KeyError
      |    +-- MemoryError
      |    +-- NameError
      |    |    +-- UnboundLocalError
      |    +-- ReferenceError
      |    +-- RuntimeError
      |    |    +-- NotImplementedError
      |    +-- SyntaxError
      |    |    +-- IndentationError
      |    |         +-- TabError
      |    +-- SystemError
      |    +-- TypeError
      |    +-- ValueError
      |         +-- UnicodeError
      |              +-- UnicodeDecodeError
      |              +-- UnicodeEncodeError
      |              +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
     +-- ImportWarning
     +-- UnicodeWarning
     +-- BytesWarning