web-dev-qa-db-fra.com

Python: meilleur moyen d’ajouter à sys.path par rapport au script en cours d’exécution

J'ai un répertoire plein de scripts (disons project/bin). J'ai aussi une bibliothèque située à project/lib et que les scripts le chargent automatiquement. Voici ce que j'utilise normalement en haut de chaque script:

#!/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. Y a-t-il une meilleure manière de faire cela?

Vraiment, ce que j'espère, c'est quelque chose d'aussi fluide:

#!/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 casserait pas lorsque mon éditeur (ou une autre personne ayant un accès validé) décidera de réorganiser les importations dans le cadre de son processus de nettoyage:

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

Cela ne porterait pas directement sur des plates-formes non-posix, mais garderait les choses propres.

77
James Harr

Si vous ne voulez pas éditer chaque fichier

  • Installez votre bibliothèque comme un python libray normal
    ou
  • Définissez PYTHONPATH sur votre lib

ou si vous souhaitez ajouter une seule ligne à chaque fichier, ajoutez une instruction d'importation en haut, par exemple.

import import_my_lib

garder import_my_lib.py dans bin et import_my_lib peut définir correctement le chemin python) vers le type de fichier lib que vous souhaitez.

20
Anurag Uniyal

C'est ce que j'utilise:

import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), "lib"))
97
jterrace

J'utilise:

import sys,os
sys.path.append(os.getcwd())
25
DusX

Créer un module wrapper project/bin/lib, qui contient ceci:

import sys, os

sys.path.insert(0, os.path.join(
    os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib'))

import mylib

del sys.path[0], sys, os

Ensuite, vous pouvez remplacer tout le croup au sommet de vos scripts avec:

#!/usr/bin/python
from lib import mylib
11
ekhumoro

Si vous ne souhaitez pas modifier le contenu du script, ajoutez le répertoire de travail en cours . to $ PYTHONPATH (voir l'exemple ci-dessous)

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

Et appelez ça un jour!

6
Khanh Hua

Utilisation de python 3.4+
Sauf utilisation de cx_freeze ou utilisation dans IDLE. ????

import sys
from pathlib import Path

sys.path.append(Path(__file__).parent / "lib")
5
GollyJer

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

Par exemple, une telle incantation magique utilise fichier. Malheureusement, si vous compressez votre script avec cx_Freeze ou 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 à partir de la commande Invite 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 accéder à ce répertoire avant l’exécution du script). Eh dieux! J'espère ne pas avoir à expliquer pourquoi cela ne fonctionnera pas si votre script Python est quelque part dans le chemin PATH et que vous l'avez exécuté en tapant simplement le nom de votre fichier de script.

Heureusement, il existe une incantation magique qui fonctionnera dans tous les cas que j'ai testés. Malheureusement, l'incantation magique est plus qu'une ligne ou deux 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 tâche facile!

2
Ben Key

Vous pouvez exécuter le script avec python -m de la racine correspondante. Et passez le "chemin des modules" en argument.

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


Un 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
0
Eyal Levin