2009-05-14 6 views
8

Al depurar, me gusta imprimir todas las entradas y salidas de una función (sé que necesito un IDE mejor, pero créanme, esto podría usarse para informar errores). Por lo tanto, me gustaría tener idealmente:¿Cómo escribirías un decorador @debuggable en python?

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

y utilizar una variable global para encender o apagar la depuración. No, tampoco te gustan los globals, supuse.

Lo mejor que puedo llegar a decir:

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 

y en funcionamiento:

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

¿Cómo puedo mejorar eso?

+0

Hay un post bastante largo sobre el tema de los decoradores de búsqueda en [Word Aligned] (http://wordaligned.org/articles/echo). –

Respuesta

22

Utilice un depurador. Seriamente. Decorar todas las funciones que desea controlar es una mala idea.

Python has a debugger included, por lo que no necesita un buen IDE.

Si no desea utilizar un depurador, puede usar el 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) 

que imprime:

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

aún más fácil sería utilizar Winpdb:

Es una independiente gráfica depurador GPL Python plataforma con soporte para la depuración remota sobre una red, múltiples hilos, modificación del espacio de nombres, depuración incorporada, comunicación encriptada y es hasta 20 veces más rápido que pdb.

Características:

  • licencia GPL. Winpdb es Software Libre.
  • Compatible con CPython 2.3 o posterior.
  • Compatible con wxPython 2.6 o posterior.
  • Plataforma independiente, y probado en Ubuntu Gutsy y Windows XP.
  • Interfaces de usuario: rpdb2 está basado en la consola, mientras que winpdb requiere wxPython 2.6 o posterior.

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

6

Estoy de acuerdo con nosklo utilizando un depurador es mucho mejor que escribir el suyo propio. Publicaré una mejora en tu código. Pero todavía creo que deberías seguir el consejo de nosklo.

clases Uso decorador para que su más ordenado depurador:

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

Secundo lo nosklo dije.

Otra cosa a notar es que su función es un poco peligroso:

b = myfunc(1,3) 

En este caso, "b" es None, porque la función decorada no devuelve nada.

7

Creo que lo que buscas no es realmente un decorador de depuración, sino más bien un decorador de registro.

Podría tener sentido utilizar Python's logging module para que pueda tener un control más fino sobre el registro. Por ejemplo, podría enviar a un archivo para analizar posteriormente el resultado.

El decorador podría entonces ser algo más como:

 

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 
 

Entonces, si se configura el registrador a la salida a stderr verías:

 

>>> 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 
 
Cuestiones relacionadas