web-dev-qa-db-fra.com

Obtenez __name__ du module de fonction appelant dans Python

Supposons que myapp/foo.py contient:

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

Et myapp/bar.py contient:

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

Je voudrais caller_name à définir sur __name__ attribut du module des fonctions appelantes (qui est 'myapp.foo') dans ce cas. Comment cela peut-il être fait?

83
Sridhar Ratnakumar

Découvrez le module d'inspection:

inspect.stack() renverra les informations de la pile.

Dans une fonction, inspect.stack()[1] renverra la pile de votre appelant. De là, vous pouvez obtenir plus d'informations sur le nom de la fonction de l'appelant, le module, etc.

Voir les documents pour plus de détails:

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

De plus, Doug Hellmann a une belle synthèse du module d'inspection dans sa série PyMOTW:

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

EDIT: Voici un code qui fait ce que vous voulez, je pense:

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

Face à un problème similaire, j'ai trouvé que sys._current_frames () du module sys contient des informations intéressantes qui peuvent vous aider, sans avoir besoin d'importer inspecter, au moins dans des cas d'utilisation spécifiques.

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

Vous pouvez ensuite "remonter" en utilisant 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__'

Pour le nom de fichier, vous pouvez également utiliser f.f_back.f_code.co_filename, comme suggéré par Mark Roddy ci-dessus. Je ne suis pas sûr des limites et des mises en garde de cette méthode (plusieurs threads seront probablement un problème) mais j'ai l'intention de l'utiliser dans mon cas.

19
Louis LC

Je ne recommande pas cela, mais vous pouvez atteindre votre objectif avec la méthode suivante:

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

Mettez ensuite à jour votre méthode existante comme suit:

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