2012-03-30 22 views
5

En algunas circunstancias, quiero imprimir la salida de estilo de depuración de esta manera:nombres impresión de variables pasadas a una función

# module test.py 
def f() 
    a = 5 
    b = 8 
    debug(a, b) # line 18 

Quiero la función debug para imprimir la siguiente:

debug info at test.py: 18 
function f 
a = 5 
b = 8 

Estoy pensando que debería ser posible utilizando el módulo de inspección para localizar el marco de la pila, luego encontrar la línea adecuada, buscar el código fuente en esa línea, obtener los nombres de los argumentos desde allí. El nombre de la función se puede obtener moviendo un marco de pila hacia arriba. (Los valores de los argumentos son fáciles de obtener: se pasan directamente a la función debug.)

¿Estoy en el camino correcto? ¿Hay alguna receta a la que pueda referirme?

Respuesta

3

se podría hacer algo a lo largo de las siguientes líneas:

import inspect 

def debug(**kwargs): 
    st = inspect.stack()[1] 
    print '%s:%d %s()' % (st[1], st[2], st[3]) 
    for k, v in kwargs.items(): 
    print '%s = %s' % (k, v) 

def f(): 
    a = 5 
    b = 8 
    debug(a=a, b=b) # line 12 

f() 

Esto muestra:

test.py:12 f() 
a = 5 
b = 8 
+1

¡Eso es agradable y simple! Pero no me importaría más complejidad si eso significa que no tengo que repetir cada nombre de variable dos veces: una vez como nombre de la palabra clave arg, y una vez como valor del argumento de la palabra clave. – max

+0

Puedes intentar llamar a 'debug (** locals())'. –

1

que estás haciendo bien en general, aunque sería más fácil de utilizar para este tipo de AOP de tareas. Básicamente, en lugar de llamar "depuración" cada vez con cada variable, puedes decorar el código con aspectos que hacen ciertas cosas sobre ciertos eventos, como al ingresar a la función para imprimir variables pasadas y su nombre.

Consulte el sitio this y anterior para post para obtener más información.

1

Sí, estás en la pista correcta. Es posible que desee mirar inspect.getargspec que devolvería una tupla llamada de args, varargs, palabras clave, valores predeterminados pasados ​​a la función.

import inspect 

def f(): 
    a = 5 
    b = 8 
    debug(a, b) 


def debug(a, b): 
    print inspect.getargspec(debug) 
f() 
0

Esto es realmente complicado. Permítanme intentar dar una respuesta más completa reutilizando this code, y la pista sobre getargspec en la respuesta de Senthil que me activó de alguna manera. Por cierto, getargspec está en desuso en Python 3.0 y getfullarcspec should be used en su lugar.

Esto funciona para mí en un Python 3.1.2 ambos con una llamada explícita a la función de depuración y con el uso de un decorador:

# from: https://stackoverflow.com/a/4493322/923794 
def getfunc(func=None, uplevel=0): 
    """Return tuple of information about a function 

    Go's up in the call stack to uplevel+1 and returns information 
    about the function found. 

    The tuple contains 
     name of function, function object, it's frame object, 
     filename and line number""" 
    from inspect import currentframe, getouterframes, getframeinfo 
    #for (level, frame) in enumerate(getouterframes(currentframe())): 
    # print(str(level) + ' frame: ' + str(frame)) 
    caller = getouterframes(currentframe())[1+uplevel] 
    # caller is tuple of: 
    # frame object, filename, line number, function 
    # name, a list of lines of context, and index within the context 
    func_name = caller[3] 
    frame = caller[0] 
    from pprint import pprint 
    if func: 
     func_name = func.__name__ 
    else: 
     func = frame.f_locals.get(func_name, frame.f_globals.get(func_name)) 
    return (func_name, func, frame, caller[1], caller[2]) 


def debug_prt_func_args(f=None): 
    """Print function name and argument with their values""" 
    from inspect import getargvalues, getfullargspec 
    (func_name, func, frame, file, line) = getfunc(func=f, uplevel=1) 
    argspec = getfullargspec(func) 
    #print(argspec) 
    argvals = getargvalues(frame) 
    print("debug info at " + file + ': ' + str(line)) 
    print(func_name + ':' + str(argvals)) ## reformat to pretty print arg values here 
    return func_name 



def df_dbg_prt_func_args(f): 
    """Decorator: dpg_prt_func_args - Prints function name and arguments 

    """ 
    def wrapped(*args, **kwargs): 
     debug_prt_func_args(f) 
     return f(*args, **kwargs) 
    return wrapped 

de uso:

@df_dbg_prt_func_args 
def leaf_decor(*args, **kwargs): 
    """Leaf level, simple function""" 
    print("in leaf") 


def leaf_explicit(*args, **kwargs): 
    """Leaf level, simple function""" 
    debug_prt_func_args() 
    print("in leaf") 


def complex(): 
    """A complex function""" 
    print("start complex") 
    leaf_decor(3,4) 
    print("middle complex") 
    leaf_explicit(12,45) 
    print("end complex") 


complex() 

y grabados:

start complex 
debug info at debug.py: 54 
leaf_decor:ArgInfo(args=[], varargs='args', keywords='kwargs', locals={'args': (3, 4), 'f': <function leaf_decor at 0x2aaaac048d98>, 'kwargs': {}}) 
in leaf 
middle complex 
debug info at debug.py: 67 
leaf_explicit:ArgInfo(args=[], varargs='args', keywords='kwargs', locals={'args': (12, 45), 'kwargs': {}}) 
in leaf 
end complex 

El decorador hace trampa un poco: ya que en wrapped obtenemos el mismo Los argumentos como la función en sí misma no importa que encontremos e informemos el ArgSpec de wrapped en getfunc y debug_prt_func_args. Este código podría embellecerse un poco, pero funciona bien ahora para los simples casos de prueba de depuración que utilicé.

Otro truco que puede hacer: Si se quite la -loop for en getfunc se puede ver que inspect le puede dar el "contexto" que en realidad es la línea de código fuente en una función fue llamado.Evidentemente, este código no muestra el contenido de ninguna variable asignada a su función, pero a veces ya es útil conocer el nombre de la variable utilizada un nivel por encima de la función llamada.

Como puede ver, con el decorador no tiene que cambiar el código dentro de la función.

Probablemente querrá imprimir bastante los args. He dejado la impresión en bruto (y también una declaración de impresión comentada) en la función, así que es más fácil jugar con ella.

Cuestiones relacionadas