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


96

Supongamos que myapp/foo.pycontiene:

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

Y myapp/bar.pycontiene:

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

Quiero caller_nameestablecer el __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_nameno puede ser__main__
Sridhar Ratnakumar

Respuestas:


123

Consulte el 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.

Consulte los documentos para obtener 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:

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

1
Entonces, ¿cómo se obtiene el __name__atributo de este módulo usando el inspectmó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

6
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 puede evitar magia como esta.
Glifo

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

6
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

También tenga en cuenta que esta lógica no funciona correctamente cuando compila su código de Python en un archivo ejecutable usando pyinstaller.
Panofish

19

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

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

Luego puede "subir" 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 sugirió Mark Roddy anteriormente. No estoy seguro de los límites y advertencias de este método (es muy probable que varios subprocesos sean un problema) pero tengo la intención de usarlo en mi caso.


2
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í.
Panofish

7
Creo que es más fácil obtener el marco anterior sys._getframe(1), en lugar de llamar sys._current_frames()(por cierto, que devuelve un mapeo de marcos para cada hilo).
hooblei

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

Prefiero usar en inspect.currentframe()lugar de sys._current_frames().values()[0].
Aran-Fey

3

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)

7
El nombre de archivo no es el mismo que__name__
Sridhar Ratnakumar
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.