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

99

J'ai un répertoire plein de scripts (disons project/bin). J'ai également une bibliothèque située dans project/libet je souhaite que les scripts la chargent automatiquement. C'est 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 encombrant, moche et doit être collé au début de chaque fichier. Y a-t-il une meilleure manière de faire cela?

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

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

import mylib

Ou encore mieux, quelque chose qui ne casserait pas lorsque mon éditeur (ou quelqu'un d'autre qui a un accès de validation) décide 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 cela garderait les choses propres.

James Harr
la source
1
Voir aussi: stackoverflow.com/questions/2349991/…
dreftymac

Réponses:

26

Si vous ne souhaitez pas modifier chaque fichier

  • Installez votre bibliothèque comme une bibliothèque Python normale
    ou
  • Réglez PYTHONPATHvotrelib

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.pydans le bac et import_my_libpeut correctement définir le chemin python sur ce que libvous voulez

Anurag Uniyal
la source
118

C'est ce que j'utilise:

import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), "lib"))
jterrace
la source
2
Je ferais sys.path.insert (0, ..) pour qu'il ne soit pas remplacé par d'autres chemins.
John Jiang
N'y a-t-il vraiment aucun moyen de faire fonctionner cela automatiquement?
Denis de Bernardy
1
Ceci est légèrement risqué. Si __file__est un nom de fichier relatif par rapport au répertoire de travail actuel (par exemple, setup.py), alors os.path.dirname(__file__)sera la chaîne vide. Pour cela et des préoccupations similaires soulevées par John Jiang , ekhumoro « est une solution plus polyvalente est fortement préférable.
Cecil Curry
29

J'utilise:

import sys,os
sys.path.append(os.getcwd())
DusX
la source
5
Je fais souvent justesys.path.append('.')
kimbo
1
Que faire si le script est exécuté à partir d'un répertoire différent? par exemple à partir du répertoire racine en donnant le chemin complet du système? puis os.getcwd () retournera "/"
obayhan
2
Ne fais jamais ça. Le répertoire de travail actuel (CWD) n'est pas garanti d'être ce que vous pensez qu'il est - en particulier dans des cas extrêmes imprévisibles que vous auriez certainement dû voir venir à un kilomètre. Faites simplement référence à la __file__place comme n'importe quel développeur quasi-sain d'esprit.
Cecil Curry
14

Créez 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 ce qui se trouve en haut de vos scripts par:

#!/usr/bin/python
from lib import mylib
ekhumoro
la source
9

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

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

Et appelez ça un jour!

Khanh Hua
la source
docs.python.org/3/tutorial/modules.html#the-module-search-path dit: "sys.path est initialisé à partir de ces emplacements: -Le répertoire contenant le script d'entrée". Je pense que ce n'est pas vrai.
J'ai tendance à faire cela, en particulier dans .envrc, de sorte qu'avec direnv, c'est même automatique et isolé.
EdvardM
8

Utilisation de python 3.4+ Interdiction de
l'utilisation de cx_freeze ou utilisation en IDLE. 😃

import sys
from pathlib import Path

sys.path.append(Path(__file__).parent / "lib")
GollyJer
la source
Compatible Python2: import pathlib import os sys.path.append (os.path.dirname ( fichier ))
michael
5

Vous pouvez exécuter le script à python -mpartir du répertoire racine approprié. Et passez le "chemin des modules" comme 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
Eyal Levin
la source
1
L'utilisation du commutateur m est la manière recommandée de le faire et non les hacks de chemin système
Mr_and_Mrs_D
4

Il y a un problème avec chaque réponse fournie qui peut être résumé comme "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 le fichier . Malheureusement, si vous empaquetez votre script avec cx_Freeze ou si vous utilisez IDLE, cela entraînera une exception.

Une autre incantation magique de ce type utilise os.getcwd (). Cela ne fonctionnera que si vous exécutez votre script à partir de 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 accéder au répertoire avant d'exécuter le script). Eh dieux! J'espère que je n'ai pas à expliquer pourquoi cela ne fonctionnera pas si votre script Python est quelque part dans le 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 simple 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 voir, ce n'est pas une tâche facile!

Ben Key
la source
0

Celui-ci fonctionne le mieux pour moi. Utilisation:

os.path.abspath('')

Sur mac, il devrait imprimer quelque chose comme:

'/Users/<your username>/<path_to_where_you_at>'

Pour obtenir le chemin abs vers le wd actuel, celui-ci est meilleur car maintenant vous pouvez monter si vous le souhaitez, comme ceci:

os.path.abspath('../')

Et maintenant:

 '/Users/<your username>/'

Donc, si vous voulez importer à utilspartir d'ici, '/Users/<your username>/'
il ne vous reste plus qu'à:

import sys
sys.path.append(os.path.abspath('../'))
Kohn1001
la source
0

Je vois un shebang dans votre exemple. Si vous exécutez vos scripts bin en tant que ./bin/foo.py, plutôt que python ./bin/foo.py, il y a une option d'utiliser le shebang pour changer la $PYTHONPATHvariable.

Cependant, vous ne pouvez pas modifier les variables d'environnement directement dans shebangs, vous aurez donc besoin d'un petit script d'aide. Mettez ceci python.shdans votre bindossier:

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

Et puis changez le shebang de votre ./bin/foo.pyêtre#!bin/python.sh

imbolc
la source
0

Lorsque nous essayons d'exécuter un fichier python avec le chemin du terminal.

import sys
#For file name
file_name=sys.argv[0]
#For first argument
dir= sys.argv[1]
print("File Name: {}, argument dir: {}".format(file_name, dir)

Enregistrez le fichier (test.py).

Système de fonctionnement.

Ouvrez le terminal et accédez au répertoire où se trouve le fichier de sauvegarde. puis écrire

python test.py "/home/saiful/Desktop/bird.jpg"

Appuyez sur Entrée

Production:

File Name: test, Argument dir: /home/saiful/Desktop/bird.jpg
bel islam
la source
0

J'utilise:

from site import addsitedir

Ensuite, peut utiliser n'importe quel répertoire relatif! addsitedir('..\lib') ; les deux points impliquent de déplacer (vers le haut) un répertoire en premier.

N'oubliez pas que tout dépend de votre répertoire de travail actuel. Si C: \ Joe \ Jen \ Becky, alors ajouteitedir ('.. \ lib') importe dans votre chemin C: \ Joe \ Jen \ lib

C:\
  |__Joe
      |_ Jen
      |     |_ Becky
      |_ lib
user7693644
la source
J'apprécie la réponse, mais cela n'ajoute pas de chemin relatif au script en cours d'exécution. Il ajoute un chemin relatif au répertoire de travail actuel
James Harr