2012-07-28 12 views
5

que tienen una función poco impresionante que se ve así:¿Cómo puedo obtener el nombre de la clase de un método vinculado de la pila del intérprete?

def verbose_print(message, *args, **kwargs): 
    """Prints `message` with a helpful prefix when in verbose mode 

    Args: 
     message (str): The message to print. Can be a format string, e.g. 
      `A %s with some %s in it` 
     *args: Variables for the message format 
     **kwargs: Keyword variables for the message format 
    """ 

    # Only print when in verbose mode 
    if not config.verbose: 
     return 

    # Ready a prefix for the message 
    try: 
     s = inspect.stack() 
     module_name = inspect.getmodule(s[1][0]).__name__ 
     func_name = s[1][3] 
     prefix = '### %s->%s' % (module_name, func_name) 
    except Exception as e: 
     prefix = '### [stack unavailable]' 

    if args: 
     message = message % args 
    elif kwargs: 
     message = message % kwargs 

    print '%s: %s' % (prefix, message) 

El punto de la función es que puedo llamarlo desde cualquier lugar con un mensaje, y si mi archivo de configuración del proyecto se establece en el modo detallado, todo los mensajes se imprimirán con un prefijo útil para mostrar dónde se llamó. Aquí está un ejemplo de una salida:

 
### avesta.webserver->check_login: Checking login for client at 127.0.0.1 
### avesta.webserver->check_login: Found credentials cookie with username: tomas, token: blablabla 
### avesta.webserver->check_login: Login valid, refreshing session 
### avesta.webserver->get_flash_memory: Fetched flash data: None 
### avesta.webserver->get: Fetched data from empty path ('previous_values', 'name'), returning '' 
### avesta.webserver->get: Fetched data from empty path ('previous_values', 'description'), returning '' 
### avesta.webserver->get: Fetched data from empty path ('validation_errors', 'name'), returning '' 

El formato es "función ### módulo->: mensaje".

Ahora la mayoría de las veces esto es realmente útil, pero no es perfecto. En el ejemplo anterior, la función "obtener" es en realidad un método vinculado de una clase, pero eso no es visible. Lo que estoy tratando de lograr es que cuando una función es un método vinculado, puedo imprimir con este formato en su lugar:

"### módulo-> ClassName.function"

Pero el problema es:

  1. solo me dan el nombre de función de la pila, por lo que no puedo comprobar si se trata de un método vinculado
  2. Incluso si tuviera una referencia de función, ¿cómo podría extrapolar el nombre de la clase es obligado ¿a?

Gracias por cualquier respuesta que pueda ayudarme a resolver esto.

Respuesta

5

Pensé que esto iba a ser fácil, pero resultó ser un poco complicado. Si tiene una referencia al método enlazado, puede obtener su nombre de clase a través del boundMethod.im_class.__name__. Sin embargo, cuando agarras la pila, no puedes obtener fácilmente una referencia al método enlazado, solo para apilar cuadros.

Sin embargo, no todo está perdido. El módulo inspect puede obtenerle argumentos de función desde un marco de pila usando la función getargvalues. Tienes que hacer trampa un poco, confiando en la convención de que los métodos siempre tienen su primer argumento llamado "self". Puede verificarlo, luego tomar el valor "self" de la función locals dict, y desde allí es fácil obtener el nombre de la clase. Intente reemplazar su bloque actual try con su código:

s = inspect.stack() 
module_name = inspect.getmodule(s[1][0]).__name__ 
func_name = s[1][3] 
arginfo = inspect.getargvalues(s[1][0]) 
if len(arginfo.args) > 0 and arginfo.args[0] == "self": 
    func_name = "%s.%s" (arginfo.locals["self"].__class__.__name__, func_name) 
prefix = '### %s->%s' % (module_name, func_name) 
+0

Brilliant! ¿Podría uno hacer menos peligroso el truco comprobando si 'self' es una instancia de clase y comprobando que de hecho tiene un método enlazado llamado 'func_name'? Si es así, ¿cómo se puede verificar eso? – Hubro

+0

Probablemente sea posible profundizar y verificar que el primer argumento sí tiene un método enlazado llamado 'nombre_func_, pero dudo que valga la pena molestarse. Creo que es más probable que termines con falsos negativos (funciones que se piensa incorrectamente que no son métodos) que falsos positivos (las funciones regulares son incorrectamente métodos) usando este enfoque. Eso es porque puedes nombrar el primer argumento de un método cualquier cosa, 'self' simplemente siendo una convención. Hmm, en realidad, ahora que lo pienso, mi código anterior no funcionará para funciones sin argumentos. Voy a editar para arreglar eso. – Blckknght

Cuestiones relacionadas