Sam & Max: Python, Django, Git et du cul » exceptions http://sametmax.com Deux développeurs en vadrouille qui se sortent les doigts du code Wed, 05 Feb 2014 14:20:37 +0000 en hourly 1 http://wordpress.org/?v=3.3.1 Log post mortem avec Python http://sametmax.com/log-post-mortem-avec-python/ http://sametmax.com/log-post-mortem-avec-python/#comments Thu, 07 Mar 2013 11:25:58 +0000 Sam http://sametmax.com/?p=5260 Il y a des mois de ça, j’avais écris un article sur atexit, un module qui permet de lancer une fonction à la fermeture de la VM Python.

Ces fonctions sont appelées même si la VM s’arrête brutalement, mais on a aucune information sur la raison de l’arrêt de la machine virtuelle Python. Et elles sont exécutées même si tout s’est bien passé.

Si vous voulez réagir au plantage de votre programme, et seulement au plantage, tout en ayant en plus des informations sur la nature du foinage :

import sys
 
def on_crash(type, value, tb):
    print type
    print value
    print tb
 
sys.excepthook = on_crash
 
declencher_erreur = 1 + "1"
 
## <type 'exceptions.TypeError'>
## unsupported operand type(s) for +: 'int' and 'str'
## <traceback object at 0x00543AF8>

Ca peut être très intéressant pour débugger un processus détaché comme par exemple votre serveur WSGI qui fait tourner Django qui décide de se petit-suicider : demandez à sys.excepthook de faire un dump de l’exception dans un fichier log , et vous pourrez voir ce qui a propoqué la crise.

Si vous êtes du genre poli, vous voudrez quand même garder l’ancien comportement de sys.excepthook, qui est toujours disponible depuis sys.__excepthook__ :

def on_crash(type, value, tb):
    # faire ce que vous voulez ici puis...
    sys.__excepthook__(type, value, tb)

Et dire que vous commenciez à croire que vous saviez presque tout sur Python. Mouarf.

flattr this!

]]>
http://sametmax.com/log-post-mortem-avec-python/feed/ 8
Pourquoi il faut spécifier l’exception qu’on gère http://sametmax.com/pourquoi-il-faut-specifier-lexception-quon-gere/ http://sametmax.com/pourquoi-il-faut-specifier-lexception-quon-gere/#comments Mon, 18 Feb 2013 14:33:01 +0000 Sam http://sametmax.com/?p=4562 On utilise beaucoup la gestion des exceptions en Python, mais le langage permet de faire des trucs apocryphes comme:

try:
    # un code qui va chier des bulles
except: # je les attrappe tous, comme les pokemons
    pass

Ceci n’est ni plus ni moins que l’équivalent de @ pour silent les erreurs en PHP.

C’est caca.

Et la raison pour laquelle j’oppose une aversion si mesurée mais néanmoins portée par des arguments forts, c’est que c’est le grand kiff de Max d’en mettre partout, et que la semaine dernière ça lui a valu un debuggage en prod.

Depuis le dernier déploiement, les logs d’un des workers d’un serveur d’encoding affichaient en boucle :

“Error accessing Database, check your connexion”

Du coup Max a cherché d’ou venait ce problème de database par les cas usuels : port ? Permission ? Network ? Mise à jour de lib de l’ORM ? Daemonisation ?

Le snippet qui provoquait ce message était (en simplifié) :

try:
    new_model = Model.objects.create(params)
except:
    self.logger.error("Error accessing Database, check your connexion")
    # + trucs pour gérer le bouzin

La raison du try / except est que parfois, sur ce serveur, et seulement sur ce serveur, la connection avec la DB distante droppait. Bug aléatoire ou de config ? Parfois il est plus simple de ne pas résoudre le problème et laisser le truc foirer de temps en temps, et juste garder un recovery. Ça évite un spécial case pour un seul serveur, et ça évite de migrer le serveur.

Sauf que ce try / except était générique, et il attrapait tout, même les rhumes.

Je lui ai demandé de virer le try / except pour voir ce qui se passait vraiment, et…

raise MissingDependency("The 'solr' backend requires the installation of 'pysolr'. Please refer to the documentation.")

Le truc qui n’a RIEN à voir avec la base de données. Solr est notre moteur de recherche, et c’est juste qu’à la création d’un objet de modèle Django, un signal est propagé pour mettre à jour l’index de Solr. Le nouveau déploiement était incomplet : il manquait une lib. La lib était importée à ce moment là, ça levait une exception, qui était attrapée bêtement par le try/except générique.

Bref, le code a été remplacé par

try:
    new_model = Model.objects.create(params)
except DataBaseError:
    self.logger.error("Error accessing Database, check your connexion")
    # + trucs pour gérer le bouzin

Si on l’avait eu depuis le début, l’erreur aurait été apparente tout de suite et rapidement corrigée.

Ne faites JAMAIS de try/except sans préciser l’exception que vous allez gérer. La seule raison pour laquelle cette feature existe, c’est qu’elle est pratique à utiliser dans un shell. En fait, même DataBaseError est trop générique comme Exception si vous voulez vraiment un controle fin.

Notez également qu’un except: pass est rarement une bonne idée. Au moins prenez le temps de logger ce qui se passe dans la clause except. Un bon log, c’est le début du bonheur.

flattr this!

]]>
http://sametmax.com/pourquoi-il-faut-specifier-lexception-quon-gere/feed/ 21
Comment recruter un développeur Python http://sametmax.com/comment-recruter-un-developpeur-python/ http://sametmax.com/comment-recruter-un-developpeur-python/#comments Sat, 27 Oct 2012 14:28:54 +0000 Sam http://sametmax.com/?p=2759 Bonjour M. Gentil. Vous venez pour la position de stagiaire ingénieur senior en periode d’essai sur 3 ans ?

C’est bien, c’est bien.

Nous avons des perspectives de progression fascinantes dans notre SS3I au carré.

J’ai juste quelques tests à vous faire passer. Trois fois rien. Simple formalité administrative. Vous comprenez, on ne peut pas embaucher pas n’importe quel Bac + 5 et le payer SMIC, comme ça sur un coup de tête.

Ce n’est pas contre vous, non.

Pourriez-vous me dire ce qu’affiche ce snippet ? (mouahahahahaha, rire diabolique intérieur)

def test():
 
    try:
        return 1 + "1"
    except TypeError:
        return "exception"
    finally:
        return "finally"
 
print test()

Et celui-là, il affiche la stack trace ou pas ?

def test():
 
    try:
        print 1 + "1"
    except TypeError:
        raise ValueError('Test')
    finally:
        return "finally"
 
print test()

Bon ok, mais si on a un générateur alors ?

def test():
 
    try:
        yield 1 + "1"
    except TypeError:
        yield 'typerror'
        return
    finally:
        yield "finally"
        return
 
    yield "Out"
 
for value in test():
    print value

Quelle exception sera catchée, dans le bout de code suivant ? (tapoter son style de manière énervante sur la table)

def test():
 
    try:
        assert 1 + "1"
    except AssertionError:
        print "assertionerror"
    except Exception:
        print "exception"
    except TypeError:
        print "typerror"
 
test()

Je vois. Donc finally est exécuté dans tous les cas ?

def test():
 
    def foo():
        foo()
 
    try:
        foo()
    except RuntimeError:
        print 'runtimeerror'
    finally:
        print 'finally'
 
test()

Vous êtes sûr ? VRAIMENT dans tous les cas ? (regard appuyé bien stressant, travaillé durant un poste de manager chez Quick)

def test():
 
    try:
        PRINTEUH !
    except SyntaxError:
        print 'syntaxerror'
    finally:
        print 'finally'
 
test()

VRAIMENT, VRAIMENT, dans tous les cas ? Mais alors, VRAIMENT ? (prendre la voix d’Alain Chabat, parce qu’arrivé à ce stade là c’est juste plus rigolo)

def test():
 
    def foo(bar):
        print bar
        return foo, bar
 
    bar = 'bar'
 
    try:
        while True:
            foo, bar = foo(bar)
 
    except RuntimeError:
        print 'runtimeerror'
    finally:
        print 'finally'
 
test()

Ne vous inquiétez pas, nous ne vous JUgeons paaaaaaas. Détendez-vous. Allez. Une petite dernière. Je vous aide. sys.exit(1) retourne le code 1.

import sys
 
def test(l=[]):
 
    print l
    try:
        return 1 + "1"
    except TypeError:
        return l or l.append(sys.exit(1))
    finally:
        print "Je sais plus là, sérieux"
        if len(l) < 1:
            test()
 
test()
test()

Bon, je sens que vous êtes fatigué. On va arrêter là peut être. Vous êtes le genre à vous en tenir là, hein ? Non. Très bien, très bien. Voici un code sur lequel on planche en interne depuis une semaine pour comprendre combien de fois il affiche finally, mais on s’est dit que vous pourriez le résoudre gratuitement pour nous:

import sys
import time
 
import multiprocessing
from Queue import Empty
 
in_queue = multiprocessing.Queue()
out_queue = multiprocessing.Queue()
 
def worker():
 
    while True:
 
        try:
            res = in_queue.get(timeout=0.1)
            if res == 'stop':
                sys.exit(1)
            print res
        except (Empty, multiprocessing.TimeoutError):
            pass
        finally:
            print 'finally'
 
 
process = multiprocessing.Process(target=worker)
process.start()
 
 
in_queue.put('test')
in_queue.put('stop')
 
print 'afterstop'
 
time.sleep(1)
 
print 'done'

Merci, ce sera tout.

Bon WE, monsieur Gentil.

Nous gardons votre CV. On vous recontactera.

flattr this!

]]>
http://sametmax.com/comment-recruter-un-developpeur-python/feed/ 7