Bienvenue sur IndexError.

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

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

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,840 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,332 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))
...