Développer le chemin de recherche Python vers une autre source

106

Je viens de rejoindre un projet avec une base de code existante assez importante. Nous développons sous Linux et n'utilisons pas et IDE. Nous parcourons la ligne de commande. J'essaie de comprendre comment faire en sorte que python recherche le bon chemin lorsque j'exécute des modules de projet. Par exemple, lorsque je lance quelque chose comme:

python someprojectfile.py

Je reçois

ImportError: no module named core.'somemodule'

J'obtiens ceci pour toutes mes importations, je suppose que c'est un problème avec le chemin.

TLDR:

Comment puis-je faire en sorte que Python recherche ~/codez/project/et tous les fichiers et dossiers pour les fichiers * .py pendant les instructions d'importation.

le chef d'orchestre
la source

Réponses:

171

Il existe plusieurs façons de procéder:

  • Définissez la variable d'environnement PYTHONPATHsur une liste de répertoires séparés par deux-points pour rechercher les modules importés.
  • Dans votre programme, utilisez sys.path.append('/path/to/search')pour ajouter les noms des répertoires dans lesquels Python recherche les modules importés. sys.pathest juste la liste des répertoires que Python recherche chaque fois qu'on lui demande d'importer un module, et vous pouvez le modifier si nécessaire (bien que je ne recommande pas de supprimer l'un des répertoires standard!). Tous les répertoires que vous placez dans la variable d'environnement PYTHONPATHseront insérés au sys.pathdémarrage de Python.
  • Utilisez site.addsitedirpour ajouter un répertoire à sys.path. La différence entre cela et simplement l'ajout est que lorsque vous utilisez addsitedir, il recherche également des .pthfichiers dans ce répertoire et les utilise pour éventuellement ajouter des répertoires supplémentaires en sys.pathfonction du contenu des fichiers. Consultez la documentation pour plus de détails.

Lequel de ceux que vous souhaitez utiliser dépend de votre situation. N'oubliez pas que lorsque vous distribuez votre projet à d'autres utilisateurs, ils l'installent généralement de manière à ce que les fichiers de code Python soient automatiquement détectés par l'importateur de Python (c'est-à-dire que les packages sont généralement installés dans le site-packagesrépertoire), donc si vous dérangez sys.pathvotre code , cela peut être inutile et peut même avoir des effets indésirables lorsque ce code s'exécute sur un autre ordinateur. Pour le développement, j'oserais penser que le réglage PYTHONPATHest généralement la meilleure façon de procéder.

Cependant, lorsque vous utilisez quelque chose qui ne fonctionne que sur votre propre ordinateur (ou lorsque vous avez des configurations non standard, par exemple parfois dans des frameworks d'applications Web), il n'est pas tout à fait rare de faire quelque chose comme

import sys
from os.path import dirname
sys.path.append(dirname(__file__))
David Z
la source
Donc, si j'avais comme disons 15 sous-répertoires, je devrais ajouter chacun individuellement?
themaestro
et pourriez-vous donner un exemple d'argument de ligne de commande pour changer PYTHONPATH?
themaestro
3
Pour définir PYTHONPATH: dans .bashrcou dans le fichier de démarrage utilisé par votre shell (si ce n'est pas Bash), écrivez export PYTHONPATH=$PYTHONPATH:$HOME/codez/project. Mais si vous avez un tas de sous-répertoires, je créerais un .pthfichier et l'utiliserais site.addsitedir. Vous pouvez créer un module sitecustomizequi peut appeler la fonction pour vous; essayez de le mettre à ~/.local/lib/python2.6/sitecustomize.py(remplacez votre version Python) pour qu'il soit importé automatiquement.
David Z
J'ai mis ce qui suit dans mon fichier .bashrc et je n'ai toujours pas de chance avec ces importations. Des idées? Comment créer un fichier .pth de toute façon? export PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / export PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / projet export PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / projet PYTHONPATH export = HOME / adaifotis / codez / project / proxies export PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / project / conf
themaestro
Essayez d'ouvrir un terminal et exécutez echo $PYTHONPATH. Si la variable d'environnement a été définie correctement, vous devriez voir une liste de répertoires séparés par deux-points. Pour plus d'informations sur les .pthfichiers, consultez la documentation du sitemodule auquel j'ai lié dans ma réponse. Il vous indique ce que le contenu devrait être et comment les utiliser.
David Z
13

Vous devriez également lire sur les packages python ici: http://docs.python.org/tutorial/modules.html .

D'après votre exemple, je suppose que vous avez vraiment un package à ~/codez/project. Le fichier __init__.pydans un répertoire python mappe un répertoire dans un espace de noms. Si vos sous-répertoires ont tous un __init__.pyfichier, il vous suffit d'ajouter le répertoire de base à votre fichier PYTHONPATH. Par exemple:

PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / projet

En plus de tester votre variable d'environnement PYTHONPATH, comme l'explique David, vous pouvez la tester en python comme ceci:

$ python
>>> import project                      # should work if PYTHONPATH set
>>> import sys
>>> for line in sys.path: print line    # print current python path

...

Andrew B.
la source
Vous voudrez peut-être mettre vos __init__.pybackticks pour qu'il soit clair que vous voulez vraiment dire __init__.py, par opposition à init.py. Juste pour éviter de dérouter les débutants. :)
antred le
4

Je sais que ce fil est un peu vieux, mais il m'a fallu un certain temps pour aller au fond de ça, alors j'ai voulu partager.

Dans mon projet, j'avais le script principal dans un répertoire parent, et, pour différencier les modules, j'ai mis tous les modules de support dans un sous-dossier appelé "modules". Dans mon script principal, j'importe ces modules comme ceci (pour un module appelé report.py):

from modules.report import report, reportError

Si j'appelle mon script principal, cela fonctionne. CEPENDANT, je voulais tester chaque module en incluant un main()dans chacun, et en appelant chacun directement, comme:

python modules/report.py

Maintenant, Python se plaint de ne pas pouvoir trouver "un module appelé modules". La clé ici est que, par défaut, Python inclut le dossier du script dans son chemin de recherche, MAIS PAS LE CWD. Donc, ce que cette erreur dit, en réalité, c'est "Je ne trouve pas de sous-dossier de modules". C'est parce qu'il n'y a pas de sous-répertoire "modules" dans le répertoire où réside le module report.py.

Je trouve que la solution la plus intéressante à cela est d'ajouter le chemin de recherche CWD dans Python en l'incluant en haut:

import sys

sys.path.append(".")

Maintenant, Python recherche le CWD (répertoire courant), trouve le sous-dossier "modules", et tout va bien.

Tom Gordon
la source
3

J'ai lu cette question à la recherche d'une réponse, et je n'ai aimé aucune d'elles.

J'ai donc écrit une solution rapide et sale. Placez-le simplement quelque part sur votre sys.path, et il ajoutera n'importe quel répertoire sous folder(à partir du répertoire de travail actuel), ou sous abspath:

#using.py

import sys, os.path

def all_from(folder='', abspath=None):
    """add all dirs under `folder` to sys.path if any .py files are found.
    Use an abspath if you'd rather do it that way.

    Uses the current working directory as the location of using.py. 
    Keep in mind that os.walk goes *all the way* down the directory tree.
    With that, try not to use this on something too close to '/'

    """
    add = set(sys.path)
    if abspath is None:
        cwd = os.path.abspath(os.path.curdir)
        abspath = os.path.join(cwd, folder)
    for root, dirs, files in os.walk(abspath):
        for f in files:
            if f[-3:] in '.py':
                add.add(root)
                break
    for i in add: sys.path.append(i)

>>> import using, sys, pprint
>>> using.all_from('py') #if in ~, /home/user/py/
>>> pprint.pprint(sys.path)
[
#that was easy
]

Et je l'aime parce que je peux avoir un dossier pour certains outils aléatoires et ne pas les faire faire partie de packages ou quoi que ce soit, et avoir toujours accès à certains (ou à tous) d'entre eux en quelques lignes de code.

Droogans
la source
3

Le moyen le plus simple que je trouve est de créer un fichier "any_name.pth" et de le mettre dans votre dossier "\ Lib \ site-packages". Vous devriez trouver ce dossier partout où python est installé.

Dans ce fichier, mettez une liste de répertoires dans lesquels vous souhaitez conserver les modules à importer. Par exemple, créez une ligne dans ce fichier comme ceci:

C: \ Utilisateurs \ exemple ... \ exemple

Vous pourrez dire que cela fonctionne en exécutant ceci en python:

import sys
for line in sys: print line

Vous verrez votre répertoire imprimé, entre autres à partir duquel vous pouvez également importer. Vous pouvez maintenant importer un fichier "mymodule.py" qui se trouve dans ce répertoire aussi facilement que:

import mymodule

Cela n'importera pas les sous-dossiers. Pour cela, vous pouvez imaginer créer un script python pour créer un fichier .pth contenant tous les sous-dossiers d'un dossier que vous définissez. Faites-le fonctionner au démarrage peut-être.

KieranPC
la source
0

Nouvelle option pour l'ancienne question.
L'installation du fail2banpaquet sur Debian semble être codé en dur pour être installé sur /usr/lib/python3/dist-packages/fail2banun chemin non sur python3 sys.path.


> python3
Python 3.7.3 (v3.7.3:ef4ec6ed12, Jun 25 2019, 18:51:50)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/usr/lib/python37.zip', '/usr/lib/python3.7', '/usr/lib/python3.7/lib-dynload', '/usr/lib/python3.7/site-packages']
>>>

donc, au lieu de simplement copier, j'ai (bash) lié la bibliothèque à des versions plus récentes.
Les futures mises à jour de l'application d'origine seront également automatiquement appliquées aux versions liées.

 if [ -d /usr/lib/python3/dist-packages/fail2ban ]
   then
      for d in /usr/lib/python3.*
      do
         [ -d ${d}/fail2ban ] || \
            ln -vs /usr/lib/python3/dist-packages/fail2ban ${d}/
      done
   fi
fcm
la source