Comment puis-je accéder au nom du module ou de la classe en cours d'exécution en cours d'exécution en Python?
Question
J'aimerais pouvoir extraire de manière dynamique le nom du module ou de la classe en cours d'exécution en cours d'exécution à partir d'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 car __ nom __
est le nom du module qui contient la fonction. Ce que j'aimerais dans le module foo
est le nom du module en cours d'exécution utilisant foo
. Donc, dans le cas ci-dessus, ce serait bar
mais si tout autre module importé foo
, je voudrais que foo
ait dynamiquement accès au nom de celui-ci. module.
Modifier: le module inspecter
semble 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 suis pas disposé à parcourir la pile pour trouver cette information - je pensais juste que Python avait peut-être déjà exposé ces données.
Modifier: Voici comment j'essaie de l'utiliser. J'ai deux applications Django différentes qui ont toutes deux besoin de consigner les erreurs dans un fichier. Disons qu'ils s'appellent "AppOne". et "AppTwo". Je souhaite également enregistrer ces fichiers dans un emplacement: " / home / hare / app_logs
". Dans chaque application, à tout moment, j'aimerais pouvoir importer mon module de journalisation et appeler la fonction de journalisation qui écrit la chaîne de journalisation dans un fichier. Toutefois, ce que je voudrais faire est de créer un répertoire sous app_logs
, qui correspond au nom de l’application actuelle ("AppOne" ou "AppTwo"), de sorte que les fichiers journaux de chaque application soient placés dans leur répertoire respectif. répertoires de journalisation.
Pour ce faire, j’ai pensé que le meilleur moyen serait que le module d’enregistrement ait accès à une sorte de variable globale indiquant le nom de l’application actuelle car il est responsable de la connaissance de l’emplacement du répertoire de journalisation parent et de la création. le répertoire de journalisation de l'application s'il n'existe pas encore.
La solution
D'après le commentaire - pas 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. À moins que votre question ne concerne un voyage dans le temps, une anti-gravité ou un mouvement perpétuel.
Étant donné que la réponse est toujours "oui", votre question est mal formulée. La vraie question est "quel bon moyen pour que mon module de journalisation connaisse le nom du client?" ou quelque chose comme ça.
La réponse est "Acceptez-le en tant que paramètre". Ne vous embêtez pas avec l'inspection ou la recherche de globaux mystérieux ou d'autres astuces.
Suivez simplement le modèle de conception de logging.getLogger () et utilisez des enregistreurs nommés explicitement. Un idiome courant est le suivant
logger= logging.getLogger( __name__ )
Cela gère presque tous les noms de journaux parfaitement.
Autres conseils
Ceci devrait fonctionner pour référencer le module actuel:
import sys
sys.modules[__name__]
Le " module en cours d'exécution " clairement est foo, car c’est ce qui contient la fonction en cours d’exécution - je pense une meilleure description de ce que vous voulez est le module de l’appelant immédiat de foo (qui peut être lui-même foo si vous appelez af () depuis une fonction appelée foo par une fonction dans la barre. La distance à laquelle vous voulez monter dépend de ce que vous voulez faire.
Dans tous les cas, en supposant que vous souhaitiez appeler immédiatement, vous pouvez l'obtenir en remontant la pile d'appels. Cela peut être accompli en appelant sys._getframe
, avec le nombre approprié de niveaux à marcher.
def f():
caller = sys._getframe(1) # Obtain calling frame
print "Called from module", caller.f_globals['__name__']
[Modifier] : en réalité, utilisez le inspecter . module comme suggéré ci-dessus est probablement une manière plus propre d’obtenir le cadre de 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 inspecter est une API plus fiable)
__ fichier __
est le chemin du module actuel de l'appel.
Pour obtenir une référence à la " __ main __ " module dans un autre:
import sys
sys.modules['__main__']
Pour obtenir ensuite le chemin du fichier du module, qui inclut son nom:
sys.modules['__main__'].__file__
Si compris dans le " __ main __ " module, utilisez simplement: __ fichier __
Pour obtenir uniquement le nom du fichier à partir du chemin du fichier:
import os
os.path.basename(file_path)
Pour séparer le nom du 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__
Je ne crois pas que ce soit possible car cela sort du champ de foo
. foo n'aura conscience que de sa portée interne car il peut être appelé par d'innombrables autres modules et applications.
L'utilisation de __ fichier __
seul vous donne un chemin relatif pour le module principal et un chemin absolu pour les modules importés. Etant conscients de cela, nous pouvons obtenir le fichier de module constamment dans les deux cas avec un peu d'aide de nos outils os.path
.
Pour le nom de fichier uniquement, utilisez __ fichier __. split (os.path.sep) [- 1]
.
Pour le chemin complet, utilisez os.path.abspath (__ fichier __)
.
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 souhaitez supprimer la ".py" de la fin, vous pouvez le faire facilement. (Mais n'oubliez pas que vous pouvez utiliser un '.pyc' à la place.)
Cela fait un certain temps que je n'ai pas python, mais je pense que vous pouvez accéder aux globaux et locaux d'un appelant via son traceback .
Si vous souhaitez uniquement le nom du fichier:
file_name = __file__.split("/")[len(__file__.split("/"))-1]
Pour obtenir le module de fichier actuel, contenant le dossier, voici ce qui a fonctionné pour moi:
import os
parts = os.path.splitext(__name__)
module_name = parts[len(parts) - 2]