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.
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.
Cela devrait fonctionner pour référencer le module actuel:
import sys
sys.modules[__name__]
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)
__file__
est le chemin du module en cours où l'appel est effectué.
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__
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.)
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.
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 .
Si vous ne voulez que le nom du fichier:
file_name = __file__.split("/")[len(__file__.split("/"))-1]
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]