2009-05-14 7 views
8

Lors du débogage, j'aime imprimer toutes les entrées et les sorties d'une fonction (je sais que j'ai besoin d'un meilleur IDE, mais humour moi, cela pourrait être utilisé pour signaler des erreurs). Donc, je voudrais idéalement avoir:Comment écririez-vous un décorateur @debuggable en python?

@debuggable 
def myfunc(argA,argB,argC): 
    return argB+1 

et d'utiliser une variable globale pour activer ou désactiver le débogage. Non, vous n'aimez pas les globals non plus, je l'ai deviné.

Le meilleur que je peux trouver est:

DEBUG = True 

def debuggable(func): 
    if DEBUG: 
     def decorated(*args): 
      print "Entering ",func.func_name 
      print " args ",args 
      ret = func(*args) 
      print ret 
      return ret 
     return decorated 
    else: 
     return func 

@debuggable 
def myfunc(this,that): 
    return this+that 

et le fonctionnement:

>>> myfunc(1,3) 
Entering myfunc 
    args (1, 3) 
4 

Comment puis-je améliorer cela?

+0

Il y a un billet de blog assez long sur le sujet de tracer des décorateurs à [Word Aligned] (http://wordaligned.org/articles/echo). –

Répondre

22

Utilisez un débogueur. Sérieusement. Décorer toutes les fonctions que vous voulez garder la trace est une mauvaise idée.

Python has a debugger included, donc vous n'avez pas besoin d'un bon IDE. Si vous ne souhaitez pas utiliser un débogueur, vous pouvez utiliser le trace function.

import sys 

@sys.settrace 
def trace_debug(frame, event, arg): 
    if event == 'call': 
     print ("calling %r on line %d, vars: %r" % 
       (frame.f_code.co_name, 
       frame.f_lineno, 
       frame.f_locals)) 
     return trace_debug 
    elif event == "return": 
     print "returning", arg 

def fun1(a, b): 
    return a + b 

print fun1(1, 2) 

qui imprime:

calling 'fun1' on line 14, vars: {'a': 1, 'b': 2} 
returning 3 
3 

encore plus facile serait d'utiliser Winpdb:

Il est un débogueur Python graphique indépendant de la plate-forme GPL avec le support pour le débogage à distance sur un réseau, plusieurs threads, modification de l'espace de noms, débogage intégré, communication cryptée et jusqu'à 20 fois plus rapide que pdb.

Caractéristiques:

  • licence GPL. Winpdb est un logiciel libre.
  • Compatible avec CPython 2.3 ou version ultérieure.
  • Compatible avec wxPython 2.6 ou version ultérieure. Plateforme indépendante et testée sur Ubuntu Gutsy et Windows XP.
  • Interfaces utilisateur: rpdb2 est basé sur la console, tandis que winpdb requiert wxPython 2.6 ou version ultérieure.

Screenshot http://winpdb.org/images/screenshot_winpdb_small.jpg

6

Je suis d'accord avec nosklo l'aide d'un débogueur est beaucoup mieux que d'écrire votre propre. Je posterai une amélioration à votre code. Mais je pense toujours que tu devrais suivre les conseils de nosklo.

Utiliser les classes de décorateur pour rendre votre débogueur plus propre:

class Debugger(object): 
    enabled = False 
    def __init__(self, func): 
     self.func = func 

    def __call__(self, *args, **kwargs): 
     if self.enabled: 
      print 'Entering', self.func.func_name 
      print ' args:', args, kwargs 
     return self.func(*args, **kwargs) 

Debugger.enabled = True 

@Debugger 
def myfunc(a, b, c, d): 
    pass 
0

Je seconde ce que nosklo dit.

Une autre chose à remarquer est que votre fonction est un peu dangereux:

b = myfunc(1,3) 

Dans ce cas, « b » est None, parce que la fonction décorée ne retourne rien.

7

Je pense que ce que vous recherchez n'est pas vraiment un décorateur de débogage, mais plutôt un décorateur de journalisation.

Il peut être judicieux d'utiliser Python's logging module afin d'avoir un meilleur contrôle de la journalisation. Par exemple, vous seriez capable de sortir dans un fichier pour analyser plus tard la sortie.

Le décorateur pourrait alors regarder quelque chose comme:

 

import logging 

logger = logging.getLogger('TraceLog') 
# TODO configure logger to write to file/stdout etc, it's level etc 


def logthis(level): 
    def _decorator(fn): 
     def _decorated(*arg,**kwargs): 
      logger.log(level, "calling '%s'(%r,%r)", fn.func_name, arg, kwargs) 
      ret=fn(*arg,**kwargs) 
      logger.log(level, "called '%s'(%r,%r) got return value: %r", fn.func_name, arg, kwargs, ret) 
      return ret 
     return _decorated 
    return _decorator 

@logthis(logging.INFO) 
def myfunc(this,that): 
    return this+that 
 

Ensuite, si vous configurez l'enregistreur à la sortie stderr vous verriez:

 

>>> logger.setLevel(logging.INFO) 
>>> handler=logging.StreamHandler() 
>>> logger.addHandler(handler) 
>>> myfunc(1,2) 
calling 'myfunc'((1, 2),{}) 
called 'myfunc'((1, 2),{}) got return value: 3 
 
Questions connexes