Python: Der beste Weg, um sys.path relativ zum aktuell ausgeführten Skript hinzuzufügen

96

Ich habe ein Verzeichnis voller Skripte (sagen wir mal project/bin). Ich habe auch eine Bibliothek in project/libund möchte, dass die Skripte sie automatisch laden. Dies ist, was ich normalerweise oben in jedem Skript verwende:

#!/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

Dies ist etwas umständlich, hässlich und muss am Anfang jeder Datei eingefügt werden. Gibt es einen besseren Weg, dies zu tun?

Ich hoffe wirklich auf etwas so Glattes wie dieses:

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

import mylib

Oder noch besser, etwas, das nicht kaputt gehen würde, wenn mein Editor (oder jemand anderes, der Zugriff festgeschrieben hat) beschließt, die Importe im Rahmen seines Bereinigungsprozesses neu zu ordnen:

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

Das würde nicht direkt auf Nicht-Posix-Plattformen portieren, aber es würde die Dinge sauber halten.

James Harr
quelle
1
Siehe auch: stackoverflow.com/questions/2349991/…
dreftymac

Antworten:

25

Wenn Sie nicht jede Datei bearbeiten möchten

  • Installieren Sie Ihre Bibliothek wie eine normale Python-Bibliothek
    oder
  • Stellen Sie PYTHONPATHauf Ihrelib

oder wenn Sie bereit sind, jeder Datei eine einzelne Zeile hinzuzufügen, fügen Sie oben eine Importanweisung hinzu, z

import import_my_lib

behalte import_my_lib.pyin bin und import_my_libkann den Python-Pfad korrekt auf das einstellen, was libdu willst

Anurag Uniyal
quelle
117

Das benutze ich:

import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), "lib"))
jterrace
quelle
2
Ich würde sys.path.insert (0, ..) ausführen, damit es nicht von anderen Pfaden überschrieben wird.
John Jiang
Gibt es wirklich keine Möglichkeit, dies automatisch auszuführen?
Denis de Bernardy
1
Dies ist etwas riskant. Wenn __file__es sich um einen relativen Dateinamen handelt, der sich auf das aktuelle Arbeitsverzeichnis bezieht (z. B. setup.py), ist os.path.dirname(__file__)dies die leere Zeichenfolge. Für diese und ähnliche von John Jiang Bedenken , ekhumoro ‚s mehr Allzweck-Lösung stark bevorzugt wird.
Cecil Curry
27

Ich benutze:

import sys,os
sys.path.append(os.getcwd())
DusX
quelle
4
Ich mache oft nursys.path.append('.')
Kimbo
1
Was ist, wenn das Skript aus einem anderen Verzeichnis ausgeführt wird? Zum Beispiel aus dem Stammverzeichnis, indem Sie den vollständigen Systempfad angeben? dann wird os.getcwd () "/"
obayhan
2
Mach das niemals. Es ist nicht garantiert, dass das aktuelle Arbeitsverzeichnis (CWD) das ist , was Sie denken - insbesondere in unvorhersehbaren Randfällen, die Sie definitiv eine Meile entfernt gesehen haben sollten. Verweisen Sie __file__stattdessen einfach wie bei jedem quasi vernünftigen Entwickler.
Cecil Curry
14

Erstellen Sie ein Wrapper-Modul project/bin/lib, das Folgendes enthält:

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

Dann können Sie alle Cruft oben in Ihren Skripten durch Folgendes ersetzen:

#!/usr/bin/python
from lib import mylib
ekhumoro
quelle
8

Wenn Sie den .Skriptinhalt in keiner Weise ändern möchten, stellen Sie dem aktuellen Arbeitsverzeichnis $ PYTHONPATH voran (siehe Beispiel unten).

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

Und nenn es einen Tag!

Khanh Hua
quelle
docs.python.org/3/tutorial/modules.html#the-module-search-path sagt: "sys.path wird von folgenden Speicherorten aus initialisiert: - Das Verzeichnis, das das Eingabeskript enthält". Ich denke das ist nicht wahr.
Ich neige dazu, dies zu tun, besonders in .envrc, so dass mit direnv das sogar automatisch und auch isoliert ist.
EdvardM
8

Verwenden von Python 3.4+ Außer
Verwendung von cx_freeze oder Verwendung in IDLE. 😃

import sys
from pathlib import Path

sys.path.append(Path(__file__).parent / "lib")
GollyJer
quelle
Python2-kompatibel: import pathlib import os sys.path.append (os.path.dirname ( Datei ))
michael
5

Sie können das Skript mit python -maus dem entsprechenden Stammverzeichnis ausführen. Und übergeben Sie den "Modulpfad" als Argument.

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


Ein anderes Beispiel:

$ 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
quelle
1
Die Verwendung des m-Schalters ist die empfohlene Methode, um dies zu tun, und nicht die sys-Pfad-Hacks
Mr_and_Mrs_D
4

Bei jeder Antwort gibt es ein Problem, das wie folgt zusammengefasst werden kann: "Fügen Sie diese magische Beschwörung einfach am Anfang Ihres Skripts hinzu. Sehen Sie, was Sie mit nur ein oder zwei Codezeilen tun können." Sie werden nicht in jeder möglichen Situation funktionieren!

Zum Beispiel verwendet eine solche magische Beschwörung eine Datei . Wenn Sie Ihr Skript mit cx_Freeze verpacken oder IDLE verwenden, führt dies leider zu einer Ausnahme.

Eine andere solche magische Beschwörung verwendet os.getcwd (). Dies funktioniert nur, wenn Sie Ihr Skript über die Eingabeaufforderung ausführen und das Verzeichnis, das Ihr Skript enthält, das aktuelle Arbeitsverzeichnis ist (dh, Sie haben den Befehl cd verwendet, um vor dem Ausführen des Skripts in das Verzeichnis zu wechseln). Eh Götter! Ich hoffe, ich muss nicht erklären, warum dies nicht funktioniert, wenn sich Ihr Python-Skript irgendwo im PATH befindet und Sie es ausgeführt haben, indem Sie einfach den Namen Ihrer Skriptdatei eingegeben haben.

Glücklicherweise gibt es eine magische Beschwörung, die in allen von mir getesteten Fällen funktioniert. Leider ist die magische Beschwörung mehr als nur ein oder zwei Codezeilen.

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)

Wie Sie sehen, ist dies keine leichte Aufgabe!

Ben Key
quelle
0

Dieser funktioniert am besten für mich. Verwenden:

os.path.abspath('')

Auf dem Mac sollte etwas gedruckt werden wie:

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

Um den ABS-Pfad zum aktuellen WD zu erhalten, ist dieser besser, da Sie jetzt nach oben gehen können, wenn Sie möchten, wie folgt:

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

Und nun:

 '/Users/<your username>/'

Wenn Sie also utilsvon hier importieren möchten, müssen '/Users/<your username>/'
Sie nur noch Folgendes tun:

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

Ich sehe einen Schebang in Ihrem Beispiel. Wenn Sie Ihre bin Skripte wie Laufen ./bin/foo.py, statt python ./bin/foo.py, gibt es eine Möglichkeit , das shebang zu ändern $PYTHONPATHvariabel.

Sie können Umgebungsvariablen jedoch nicht direkt in shebangs ändern, sodass Sie ein kleines Hilfsskript benötigen. Legen Sie dies python.shin Ihren binOrdner:

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

Und dann die shebang Ihrer ändern ./bin/foo.pyzu sein#!bin/python.sh

imbolc
quelle
0

Wenn wir versuchen, eine Python-Datei mit einem Pfad vom Terminal aus auszuführen.

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)

Speichern Sie die Datei (test.py).

Laufendes System.

Öffnen Sie das Terminal und gehen Sie in das Verzeichnis, in dem sich die Datei befindet. dann schreibe

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

Drücke Enter

Ausgabe:

File Name: test, Argument dir: /home/saiful/Desktop/bird.jpg
schöner Islam
quelle