web-dev-qa-db-fra.com

Comment puis-je accéder au module d'exécution en cours ou au nom de classe en Python?

Je voudrais pouvoir récupérer dynamiquement le nom du module en cours d'exécution ou de la classe depuis un module importé. Voici du code:

foo.py:

def f():
    print __name__

bar.py:

from foo import f

def b(): f()

Cela ne fonctionne évidemment pas comme __name__ est le nom du module qui contient la fonction. Ce que j'aimerais avoir accès à l'intérieur du module foo est le nom du module en cours d'exécution qui utilise foo. Ainsi, dans le cas ci-dessus, ce serait bar mais si un autre module importait foo j'aimerais que foo ait dynamiquement accès au nom de ce module.

Edit: Le module inspect semble assez prometteur mais ce n'est pas exactement ce que je cherchais. Ce que j'espérais, c'était une sorte de variable globale ou au niveau de l'environnement à laquelle je pourrais accéder qui contiendrait le nom du module en cours d'exécution. Non pas que je ne veuille pas parcourir la pile pour trouver ces informations - je pensais juste que Python peut avoir déjà exposé ces données.

Edit: Voici comment j'essaie d'utiliser cela. J'ai deux Django applications différentes qui ont toutes deux besoin de consigner les erreurs dans un fichier. Disons qu'elles sont appelées "AppOne" et "AppTwo". J'ai également un emplacement auquel je voudrais me connecter ces fichiers: "/home/hare/app_logs ". Dans chaque application, à un moment donné, j'aimerais pouvoir importer mon module d'enregistreur et appeler la fonction de journal qui écrit la chaîne de journal dans un fichier. Cependant, ce que je voudrais faire, c'est créer un répertoire sous app_logs qui est le nom de l'application en cours ("AppOne" ou "AppTwo") afin que les fichiers journaux de chaque application soient placés dans leurs répertoires de journalisation respectifs.

Pour ce faire, je pensais que le meilleur moyen serait que le module d'enregistreur ait accès à une sorte de variable globale qui dénote le nom de l'application actuelle car il est responsable de connaître l'emplacement du répertoire de journalisation parent et de créer la journalisation de l'application répertoire s'il n'existe pas encore.

40
Andrew Hare

Du commentaire - pas de la question.

Je suis simplement curieux de voir si ce que j'essaie de faire est possible.

La réponse à "est-ce possible" est toujours "oui". Toujours. Sauf si votre question implique un voyage dans le temps, de l'anti-gravité ou un mouvement perpétuel.

Puisque la réponse est toujours "oui", votre question est mal formée. La vraie question est "quel est le bon moyen pour que mon module de journalisation connaisse le nom du client?" ou quelque chose comme ça.

La réponse est "Acceptez-le comme paramètre". Ne plaisante pas avec l'inspection ou la recherche de mystérieux globaux ou d'autres astuces.

Suivez simplement le modèle de conception de logging.getLogger () et utilisez des enregistreurs nommés explicitement. Un idiome commun est le suivant

logger= logging.getLogger( __)

Cela gère presque tous les noms de journaux parfaitement.

48
S.Lott

Cela devrait fonctionner pour référencer le module actuel:

import sys
sys.modules[__name__]
32
newtover

Le "module en cours d'exécution" est clairement foo, car c'est ce qui contient la fonction en cours d'exécution - je pense qu'une meilleure description de ce que vous voulez est le module de l'appelant immédiat de foo (qui peut lui-même être foo si vous appelez un f() à partir d'une fonction dans foo appelée par une fonction dans bar. La distance que vous souhaitez atteindre dépend de ce que vous voulez.

Dans tous les cas, en supposant que vous voulez l'appelant immédiat, vous pouvez l'obtenir en remontant la pile des appels. Cela peut être accompli en appelant sys._getframe , avec le nombre approprié de niveaux à parcourir.

def f():
    caller = sys._getframe(1)  # Obtain calling frame
    print "Called from module", caller.f_globals['__name__']

[Edit] : En fait, l'utilisation du module inspect comme suggéré ci-dessus est probablement un moyen plus propre d'obtenir le cadre de la pile. Le code équivalent est:

def f():
    caller = inspect.currentframe().f_back
    print "Called from module", caller.f_globals['__name__']

(sys._getframe est documenté comme étant à usage interne - le module d'inspection est une API plus fiable)

20
Brian

__file__ est le chemin du module en cours où l'appel est effectué.

12
utku_karatas

Je pense que ce que vous voulez utiliser est le module inspect , pour inspecter la pile d'exécution python. Vérifiez ceci tutoriel . Je pense que fournit un exemple presque exact de ce que vous voulez faire.

11
nstehr

Pour obtenir une référence au module "__main__" dans un autre:

import sys
sys.modules['__main__']

Pour obtenir ensuite le chemin d'accès au fichier du module, qui inclut son nom:

sys.modules['__main__'].__file__

Si dans le module "__main__", utilisez simplement: __file__

Pour obtenir uniquement le nom de fichier à partir du chemin d'accès au fichier:

import os
os.path.basename(file_path)

Pour séparer le nom de fichier de son extension:

file_name.split(".")[0]

Pour obtenir le nom d'une instance de classe:

instance.__class__.__name__

Pour obtenir le nom d'une classe:

class.__name__
4
Sjshovan

L'utilisation de __file__ Seul vous donne un chemin relatif pour le module principal et un chemin absolu pour les modules importés. Conscient de cela, nous pouvons obtenir le fichier de module en permanence dans les deux sens avec un peu d'aide à partir de nos outils os.path.

Pour le nom de fichier, utilisez uniquement __file__.split(os.path.sep)[-1].

Pour un chemin complet, utilisez os.path.abspath(__file__).

Démo:

/tmp $ cat f.py
from pprint import pprint
import os
import sys

pprint({
    'sys.modules[__name__]': sys.modules[__name__],
    '__file__': __file__,
    '__file__.split(os.path.sep)[-1]': __file__.split(os.path.sep)[-1],
    'os.path.abspath(__file__)': os.path.abspath(__file__),
})

/tmp $ cat i.py
import f

Résultats:

## on *Nix ##

/tmp $ python3 f.py
{'sys.modules[__name__]': <module '__main__' from 'f.py'>,
 '__file__': 'f.py',
 '__file__.split(os.path.sep)[-1]': 'f.py',
 'os.path.abspath(__file__)': '/tmp/f.py'}

/tmp $ python3 i.py
{'sys.modules[__name__]': <module 'f' from '/tmp/f.pyc'>,
 '__file__': '/tmp/f.pyc',
 '__file__.split(os.path.sep)[-1]': 'f.pyc',
 'os.path.abspath(__file__)': '/tmp/f.pyc'}

## on Windows ##

PS C:\tmp> python3.exe f.py
{'sys.modules[__name__]': <module '__main__' from 'f.py'>,
 '__file__': 'f.py',
 '__file__.split(os.path.sep)[-1]': 'f.py',
 'os.path.abspath(__file__)': 'C:\\tools\\cygwin\\tmp\\f.py'}

PS C:\tmp> python3.exe i.py
{'sys.modules[__name__]': <module 'f' from 'C:\\tools\\cygwin\\tmp\\f.py'>,
 '__file__': 'C:\\tools\\cygwin\\tmp\\f.py',
 '__file__.split(os.path.sep)[-1]': 'f.py',
 'os.path.abspath(__file__)': 'C:\\tools\\cygwin\\tmp\\f.py'}

Si vous voulez retirer le ".py" de la fin, vous pouvez le faire facilement. (Mais n'oubliez pas que vous pouvez exécuter un '.pyc' à la place.)

3
Bruno Bronosky

Je ne pense pas que ce soit possible car cela est hors de portée de foo. foo ne connaîtra que sa portée interne car il peut être appelé par d'innombrables autres modules et applications.

3
Soviut

Cela fait un moment que je n'ai pas fait de python, mais je crois que vous pouvez accéder aux globaux et aux locaux d'un appelant via son traceback .

2
Mr Fooz

Si vous ne voulez que le nom du fichier:

file_name = __file__.split("/")[len(__file__.split("/"))-1]
1
RiccardoCh

Pour obtenir le module de fichiers actuel, contenant le dossier, voici ce qui a fonctionné pour moi:

 import os
 parts = os.path.splitext(__name__)
 module_name = parts[len(parts) - 2]
0
sveilleux2