Récupération du chemin du module python


Answers

Il y a un module inspect en python.

Documentation officielle

Le module inspect fournit plusieurs fonctions utiles pour obtenir des informations sur les objets vivants tels que les modules, les classes, les méthodes, les fonctions, les retraçages, les objets d'image et les objets de code. Par exemple, il peut vous aider à examiner le contenu d'une classe, récupérer le code source d'une méthode, extraire et formater la liste des arguments pour une fonction, ou obtenir toutes les informations dont vous avez besoin pour afficher un retraçage détaillé.

Exemple:

>>> import os
>>> import inspect
>>> inspect.getfile(os)
'/usr/lib64/python2.7/os.pyc'
>>> inspect.getfile(inspect)
'/usr/lib64/python2.7/inspect.pyc'
>>> os.path.dirname(inspect.getfile(inspect))
'/usr/lib64/python2.7'
Question

Je veux détecter si le module a changé. Maintenant, en utilisant inotify est simple, vous avez juste besoin de connaître le répertoire dont vous voulez recevoir les notifications.

Comment puis-je récupérer le chemin d'un module en python?




Je vais essayer d'aborder quelques variantes de cette question:

  1. trouver le chemin du script appelé
  2. trouver le chemin du script en cours d'exécution
  3. trouver le répertoire du script appelé

(Certaines de ces questions ont été posées sur SO, mais ont été fermées en double et redirigées ici.)

Mises en garde de l'utilisation de __file__

Pour un module que vous avez importé:

import something
something.__file__ 

retournera le chemin absolu du module. Cependant, étant donné le script suivant foo.py:

#foo.py
print '__file__', __file__

L'appeler avec 'python foo.py' retournera simplement 'foo.py'. Si vous ajoutez un shebang:

#!/usr/bin/python 
#foo.py
print '__file__', __file__

et appelez-le en utilisant ./foo.py, il renverra './foo.py'. Appel à partir d'un répertoire différent, (par exemple mettre foo.py dans la barre de répertoire), puis en appelant soit

python bar/foo.py

ou en ajoutant un shebang et en exécutant le fichier directement:

bar/foo.py

retournera 'bar / foo.py' (le chemin relatif ).

Trouver le répertoire

Maintenant, aller de là pour obtenir le répertoire, os.path.dirname(__file__) peut aussi être difficile. Au moins sur mon système, il renvoie une chaîne vide si vous l'appelez du même répertoire que le fichier. ex.

# foo.py
import os
print '__file__ is:', __file__
print 'os.path.dirname(__file__) is:', os.path.dirname(__file__)

va sortir:

__file__ is: foo.py
os.path.dirname(__file__) is: 

En d'autres termes, il retourne une chaîne vide, donc cela ne semble pas fiable si vous voulez l'utiliser pour le fichier en cours (par opposition au fichier d'un module importé). Pour contourner cela, vous pouvez l'enrouler dans un appel à abspath:

# foo.py
import os
print 'os.path.abspath(__file__) is:', os.path.abspath(__file__)
print 'os.path.dirname(os.path.abspath(__file__)) is:', os.path.dirname(os.path.abspath(__file__))

qui produit quelque chose comme:

os.path.abspath(__file__) is: /home/user/bar/foo.py
os.path.dirname(os.path.abspath(__file__)) is: /home/user/bar

Notez que abspath () ne résout PAS les liens symboliques. Si vous voulez faire cela, utilisez plutôt realpath (). Par exemple, en créant un lien symbolique file_import_testing_link pointant vers file_import_testing.py, avec le contenu suivant:

import os
print 'abspath(__file__)',os.path.abspath(__file__)
print 'realpath(__file__)',os.path.realpath(__file__)

L'exécution imprimera des chemins absolus quelque chose comme:

abspath(__file__) /home/user/file_test_link
realpath(__file__) /home/user/file_test.py

file_import_testing_link -> file_import_testing.py

Utiliser inspecter

@SummerBreeze mentionne l'utilisation du module inspect .

Cela semble bien fonctionner, et est assez concis, pour les modules importés:

import os
import inspect
print 'inspect.getfile(os) is:', inspect.getfile(os)

retourne docilement le chemin absolu. Cependant, pour trouver le chemin du script en cours d'exécution, je n'ai pas trouvé le moyen de l'utiliser.




J'ai donc passé pas mal de temps à essayer de le faire avec py2exe. Le problème était d'obtenir le dossier de base du script, qu'il soit exécuté en tant que script python ou en tant qu'exécutable py2exe. Aussi pour que ça marche, que ce soit à partir du dossier courant, d'un autre dossier ou (c'était le plus difficile) du chemin du système.

Finalement, j'ai utilisé cette approche, en utilisant sys.frozen comme un indicateur de fonctionnement dans py2exe:

import os,sys
if hasattr(sys,'frozen'): # only when running in py2exe this exists
    base = sys.prefix
else: # otherwise this is a regular python script
    base = os.path.dirname(os.path.realpath(__file__))



Utilitaire de ligne de commande

Vous pouvez le modifier en un utilitaire de ligne de commande,

python-which <package name>

Créez /usr/local/bin/python-which

#!/usr/bin/env python

import importlib
import os
import sys

args = sys.argv[1:]
if len(args) > 0:
    module = importlib.import_module(args[0])
    print os.path.dirname(module.__file__)

Rendez-le exécutable

sudo chmod +x /usr/local/bin/python-which



C'était trivial.

Chaque module a une variable __file__ qui montre son chemin relatif d'où vous êtes en ce moment.

Par conséquent, obtenir un répertoire pour le module pour le notifier est simple comme:

os.path.dirname(__file__)



De l'intérieur des modules d'un paquet python j'ai dû faire référence à un fichier qui résidait dans le même répertoire que le paquet. Ex.

some_dir/
  maincli.py
  top_package/
    __init__.py
    level_one_a/
      __init__.py
      my_lib_a.py
      level_two/
        __init__.py
        hello_world.py
    level_one_b/
      __init__.py
      my_lib_b.py

Donc, ci-dessus, j'ai dû appeler maincli.py du module my_lib_a.py en sachant que top_package et maincli.py sont dans le même répertoire. Voici comment je reçois le chemin de maincli.py:

import sys
import os
import imp


class ConfigurationException(Exception):
    pass


# inside of my_lib_a.py
def get_maincli_path():
    maincli_path = os.path.abspath(imp.find_module('maincli')[1])
    # top_package = __package__.split('.')[0]
    # mod = sys.modules.get(top_package)
    # modfile = mod.__file__
    # pkg_in_dir = os.path.dirname(os.path.dirname(os.path.abspath(modfile)))
    # maincli_path = os.path.join(pkg_in_dir, 'maincli.py')

    if not os.path.exists(maincli_path):
        err_msg = 'This script expects that "maincli.py" be installed to the '\
        'same directory: "{0}"'.format(maincli_path)
        raise ConfigurationException(err_msg)

    return maincli_path

Basé sur l'affichage par PlasmaBinturong j'ai modifié le code.




J'aimerais contribuer avec un scénario commun (en Python 3) et explorer quelques approches.

La fonction intégrée open() accepte le chemin relatif ou absolu comme premier argument. Le chemin relatif est considéré comme relatif au répertoire de travail actuel , il est donc recommandé de transmettre le chemin absolu au fichier.

Dit simplement, si vous exécutez le script suivant, il n'est pas garanti que le fichier example.txt sera créé dans le même répertoire:

with open('example.txt', 'w'):
    pass

Pour corriger ce code, nous devons obtenir le chemin absolu du script. Pour assurer que le chemin soit absolu, nous utilisons simplement la fonction os.path.realpath() . Pour obtenir le chemin du script, plusieurs fonctions communes renvoient différents résultats de chemin:

  • os.getcwd()
  • os.path.realpath('example.txt')
  • sys.argv[0]
  • __file__

Les deux fonctions os.getcwd() et os.path.realpath() renvoient les résultats du chemin en fonction du répertoire de travail courant . Généralement pas ce que nous voulons. Le premier élément de la liste sys.argv est le chemin du script racine (le script que vous exécutez), que vous appeliez la liste dans le script racine lui-même ou dans l'un de ses modules. Il pourrait être utile dans certaines situations. La variable __file__ contient le chemin du module à partir duquel elle a été appelée.

Le code suivant crée correctement un fichier example.txt dans le même répertoire que le script:

filedir = os.path.dirname(os.path.realpath(__file__))
filepath = os.path.join(filedir, 'example.txt')

with open(filepath, 'w'):
    pass