Obtenga __name__ del módulo de la función de llamada en Python

4 minutos de lectura

Suponer myapp/foo.py contiene:

def info(msg):
    caller_name = ????
    print '[%s] %s' % (caller_name, msg)

Y myapp/bar.py contiene:

import foo
foo.info('Hello') # => [myapp.bar] Hello

quiero caller_name para ser ajustado a la __name__ atributo del módulo de funciones de llamada (que es ‘myapp.foo’) en este caso. ¿Cómo se puede hacer esto?

  • Suponga que algún otro script de punto de entrada invoca bar.py .. y por lo tanto caller_name no puede ser __main__

    –Sridhar Ratnakumar

    08 julio 2009 a las 0:36

Echa un vistazo al módulo de inspección:

inspect.stack() devolverá la información de la pila.

Dentro de una función, inspect.stack()[1] devolverá la pila de su llamador. Desde allí, puede obtener más información sobre el nombre de la función, el módulo, etc. de la persona que llama.

Ver los documentos para más detalles:

http://docs.python.org/library/inspect.html

Además, Doug Hellmann tiene una buena descripción del módulo de inspección en su serie PyMOTW:

http://pymotw.com/2/inspect/index.html#module-inspect

EDITAR: Aquí hay un código que hace lo que quieres, creo:

import inspect 

def info(msg):
    frm = inspect.stack()[1]
    mod = inspect.getmodule(frm[0])
    print '[%s] %s' % (mod.__name__, msg)

  • Entonces, ¿cómo obtienes el __name__ atributo de este módulo usando el inspect ¿módulo? Por ejemplo, ¿cómo vuelvo myapp.foo (no myapp/foo.py) en mi ejemplo anterior? Ya intenté usar el módulo de inspección antes de publicar en SO.

    –Sridhar Ratnakumar

    08 julio 2009 a las 3:38

  • Tenga en cuenta que esto interactuará de manera extraña con los ganchos de importación, no funcionará en ironpython y puede comportarse de manera sorprendente en jython. Es mejor si puedes evitar la magia como esta.

    – Glifo

    09 jul. 2009 a las 11:24

  • También tenga en cuenta que mantener una referencia a un marco de pila puede evitar que el GC de Python funcione correctamente. Ver advertencia aquí: docs.python.org/library/inspect.html#the-interpreter-stack

    – Kamil Kisiel

    09 nov.

  • Tenga en cuenta que si la función de llamada está decorada (@…), debe acceder inspect.stack()[2] para la persona que llama real.

    –Amir Ali Akbari

    03 feb.

  • También tenga en cuenta que esta lógica no funciona correctamente cuando compila su código python en un exe usando pyinstaller.

    – pez pano

    16 oct.

Enfrentado a un problema similar, he encontrado que sys._current_frames() del módulo sys contiene información interesante que puede ayudarte, sin necesidad de importar inspeccionar, al menos en casos de uso específicos.

>>> sys._current_frames()
{4052: <frame object at 0x03200C98>}

Luego puede “mover hacia arriba” usando f_back :

>>> f = sys._current_frames().values()[0]
>>> # for python3: f = list(sys._current_frames().values())[0]

>>> print f.f_back.f_globals['__file__']
'/base/data/home/apps/apricot/1.6456165165151/caller.py'

>>> print f.f_back.f_globals['__name__']
'__main__'

Para el nombre de archivo, también puede usar f.f_back.f_code.co_filename, como lo sugirió Mark Roddy arriba. No estoy seguro de los límites y advertencias de este método (lo más probable es que varios subprocesos sean un problema), pero tengo la intención de usarlo en mi caso.

  • nota: el código inspect.stack FALLA después de compilar a exe usando pyinstaller, pero usando sys._current_frames FUNCIONA BIEN… así que esta es la técnica preferida para mí.

    – pez pano

    16 oct.

  • Creo que es más fácil obtener el cuadro anterior por sys._getframe(1), en lugar de llamar sys._current_frames() (por cierto, que devuelve una asignación de fotogramas para cada subproceso).

    – hooblei

    06 oct.

  • Gracias hooblei, aún no lo he probado, pero parece muy útil para situaciones de subprocesos múltiples.

    – Luis L. C.

    06 oct.

  • prefiero usar inspect.currentframe() en vez de sys._current_frames().values()[0].

    – Aran Fey

    28 abr.

No recomiendo hacer esto, pero puede lograr su objetivo con el siguiente método:

def caller_name():
    frame=inspect.currentframe()
    frame=frame.f_back.f_back
    code=frame.f_code
    return code.co_filename

Luego actualice su método existente de la siguiente manera:

def info(msg):
    caller = caller_name()
    print '[%s] %s' % (caller, msg)

  • El nombre del archivo no es el mismo que __name__

    –Sridhar Ratnakumar

    08 julio 2009 a las 3:28

.

¿Ha sido útil esta solución?