Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Gestion fine des Exceptions

+5 votes
>>> import time
>>> import arrow
>>> import datetime
>>> from datetime import datetime

>>> date_de_sortie_des_goonies = arrow.get(datetime(1985, 12, 4), 'US/Pacific')

>>> date_de_sortie_des_goonies
<Arrow [1985-12-04T00:00:00-08:00]>


>>> date_de_sortie_des_goonies = arrow.get(datetime(1985, 28, 4))
Traceback (most recent call last):
  File "<pyshell#24>", line 1, in <module>
    date_de_sortie_des_goonies = arrow.get(datetime(1985, 28, 4))
ValueError: month must be in 1..12


>>> date_de_sortie_des_goonies = arrow.get(datetime(1985, 12, 50))
Traceback (most recent call last):
  File "<pyshell#26>", line 1, in <module>
    date_de_sortie_des_goonies = arrow.get(datetime(1985, 12, 50))
ValueError: day is out of range for month

Ici on peut obtenir au moins 2 Value error différents, comment faire un try except qui gère les deux cas de façon distincte, courte et pythonique ?

Je pourrais contourner la difficulté avec un objet MaDate, avec plusieurs attributs Année, Mois, Jour, que je renseigne successivement avec des input et vérifier chaque input ; c'est chiant hein ?

demandé 28-Jun-2015 par buffalo974 (2,956 points)

1 Réponse

+5 votes
 
Meilleure réponse

Le problème est extrèmement spécifique à l'exception, aussi doutés-je qu'il existe un traitement générique.

Voilà une solution simple et efficace… tant que rien ne change dans l'API de datetime :

import time, arrow, datetime
from datetime import datetime

def problem_origin(e):
    for origin in ('month', 'day'):
        if e.args[0].startswith(origin):
            return origin

try:
    goo = arrow.get(datetime(1985, 17, 4))
except ValueError as e:
    print(problem_origin(e))

try:
    goo = arrow.get(datetime(1985, 11, 34))
except ValueError as e:
    print(problem_origin(e))

Sinon, je serais d'avis de prendre le problème à la racine :

import time, arrow, datetime
from datetime import datetime as datetime_system


def datetime_wrapper(func):
    def problem_origin(e):
        for origin in ('month', 'day'):
            if e.args[0].startswith(origin):
                return origin

    def wrapper(year, month, day):
        try:
            ret = arrow.get(datetime_system(year, month, day))
        except ValueError as e:
            if problem_origin(e) == 'day':
                raise ValueError("unexpected day value")
            else:  # month fucked up
                raise ValueError("unexpected month value")
        return ret
    return wrapper

datetime = datetime_wrapper(datetime_system)


try:
    goo = arrow.get(datetime(1985, 17, 4))
except ValueError as e:
    print(e)

try:
    goo = arrow.get(datetime(1985, 11, 34))
except ValueError as e:
    print(e)

Dans ce cas, pas nécessairement besoin de lever une exception : le décorateur peut opérer des changements, appeler un logger,…
Il est aussi possible (peut-être même plus sûr) de tenter un truc comme ça pour la fonction problem_origin:

def problem_origin_from(year, month, day):
    try:
        return arrow.get(datetime_system(year, month, day))
    except ValueError as e:
        try:
            month = 1
            arrow.get(datetime_system(year, month, day))
            print("unexpected month value")
        except ValueError as e:
            print("unexpected day value")

Le principe : plutôt d'extrapoler soit même les conditions de succès de datetime, on va modifier un par un les arguments de manière à éliminer l'erreur.
Dans ces conditions, c'est moche mais on évite d'avoir à recoder soit même un ersatz de vérification de date.

répondu 28-Jun-2015 par lucas (2,340 points)
sélectionné 29-Jun-2015 par buffalo974

joli le wrapper.

Le constructeur de datetime accepte d'autres parametres que year/month/day
Pour etre plus générique, on peut jouer avec *args et **kwargs :

def datetime_wrapper(func):
    def problem_origin(e):
        # ...

    def wrapper(*args, **kwargs): # here
        try:
            ret = arrow.get(datetime_system(*args, **kwargs)) # and here
        except ValueError as e:
              # ...
        return ret
    return wrapper

Ce qui permettras d'utilisr l'API originale de maniere transparente :

matrix = arrow.get(datetime(1999, 3, 31))
metropolis = arrow.get(datetime(year=1927, hour=10, minute=30, day=13,  month=3))
...