139 votes

Python : Meilleure façon d'ajouter à sys.path relatif au script en cours d'exécution

J'ai un répertoire rempli de scripts (disons project/bin ). Je dispose également d'une bibliothèque située à project/lib et que les scripts le chargent automatiquement. C'est ce que j'utilise normalement au début de chaque scripts :

#!/usr/bin/python
from os.path import dirname, realpath, sep, pardir
import sys
sys.path.append(dirname(realpath(__file__)) + sep + pardir + sep + "lib")

# ... now the real code
import mylib

C'est un peu lourd, moche, et il faut le coller au début de chaque fichier. Existe-t-il une meilleure façon de procéder ?

Ce que j'espère vraiment, c'est quelque chose d'aussi fluide que cela :

#!/usr/bin/python
import sys.path
from os.path import pardir, sep
sys.path.append_relative(pardir + sep + "lib")

import mylib

Ou mieux encore, quelque chose qui ne se briserait pas lorsque mon éditeur (ou quelqu'un d'autre qui a un accès autorisé) déciderait de réorganiser les importations dans le cadre de son processus de nettoyage :

#!/usr/bin/python --relpath_append ../lib
import mylib

Cette solution ne serait pas directement portable sur les plates-formes non-posix, mais elle permettrait de garder les choses propres.

16voto

Khanh Hua Points 33

Si vous ne souhaitez pas modifier le contenu de script de quelque manière que ce soit, ajoutez le répertoire de travail actuel . à $PYTHONPATH (voir l'exemple ci-dessous)

PYTHONPATH=.:$PYTHONPATH alembic revision --autogenerate -m "First revision"

Et c'est tout !

15voto

eyalev Points 60

Vous pouvez exécuter le script avec python -m à partir du répertoire racine correspondant. Et passez le "chemin des modules" comme argument.

Exemple : $ python -m module.sub_module.main # Notice there is no '.py' at the end.


Autre exemple :

$ tree  # Given this file structure
.
├── bar
│   ├── __init__.py
│   └── mod.py
└── foo
    ├── __init__.py
    └── main.py

$ cat foo/main.py
from bar.mod import print1
print1()

$ cat bar/mod.py
def print1():
    print('In bar/mod.py')

$ python foo/main.py  # This gives an error
Traceback (most recent call last):
  File "foo/main.py", line 1, in <module>
    from bar.mod import print1
ImportError: No module named bar.mod

$ python -m foo.main  # But this succeeds
In bar/mod.py

6voto

gildniy Points 63

C'est ainsi que je procède souvent :

import os
import sys

current_path = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.join(current_path, "lib"))

4voto

Ben Key Points 388

Il y a un problème avec chaque réponse fournie qui peut être résumée par "ajoutez simplement cette incantation magique au début de votre script. Voyez ce que vous pouvez faire avec seulement une ligne ou deux de code". Ils ne fonctionneront pas dans toutes les situations possibles !

Par exemple, l'une de ces incantations magiques utilise __file__ . Malheureusement, si vous empaquetez votre script en utilisant cx_Freeze ou si vous utilisez IDLE, il en résultera une exception.

Une autre incantation magique de ce type utilise os.getcwd(). Cela ne fonctionnera que si vous exécutez votre script depuis l'invite de commande et que le répertoire contenant votre script est le répertoire de travail actuel (c'est-à-dire que vous avez utilisé la commande cd pour entrer dans le répertoire avant d'exécuter le script). Eh, les dieux ! J'espère ne pas avoir à expliquer pourquoi cela ne fonctionnera pas si votre script Python se trouve quelque part dans le PATH et que vous l'avez lancé en tapant simplement le nom de votre fichier script.

Heureusement, il existe une incantation magique qui fonctionne dans tous les cas que j'ai testés. Malheureusement, cette incantation magique ne se résume pas à une ou deux lignes de code.

import inspect
import os
import sys

# Add script directory to sys.path.
# This is complicated due to the fact that __file__ is not always defined.

def GetScriptDirectory():
    if hasattr(GetScriptDirectory, "dir"):
        return GetScriptDirectory.dir
    module_path = ""
    try:
        # The easy way. Just use __file__.
        # Unfortunately, __file__ is not available when cx_Freeze is used or in IDLE.
        module_path = __file__
    except NameError:
        if len(sys.argv) > 0 and len(sys.argv[0]) > 0 and os.path.isabs(sys.argv[0]):
            module_path = sys.argv[0]
        else:
            module_path = os.path.abspath(inspect.getfile(GetScriptDirectory))
            if not os.path.exists(module_path):
                # If cx_Freeze is used the value of the module_path variable at this point is in the following format.
                # {PathToExeFile}\{NameOfPythonSourceFile}. This makes it necessary to strip off the file name to get the correct
                # path.
                module_path = os.path.dirname(module_path)
    GetScriptDirectory.dir = os.path.dirname(module_path)
    return GetScriptDirectory.dir

sys.path.append(os.path.join(GetScriptDirectory(), "lib"))
print(GetScriptDirectory())
print(sys.path)

Comme vous pouvez le constater, ce n'est pas une mince affaire !

0voto

imbolc Points 99

Je vois tout un tas de choses dans votre exemple. Si vous exécutez votre bin scripts en tant que ./bin/foo.py plutôt que python ./bin/foo.py il est possible d'utiliser le shebang pour modifier les données de la $PYTHONPATH variable.

Vous ne pouvez pas modifier les variables d'environnement directement dans les shebangs, vous aurez donc besoin d'un petit script. Mettez ceci python.sh dans votre bin dossier :

#!/usr/bin/env bash
export PYTHONPATH=$PWD/lib
exec "/usr/bin/python" "$@"

Puis modifiez l'ensemble de votre ./bin/foo.py ser #!bin/python.sh

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X