Erweitern Sie den Python-Suchpfad zu einer anderen Quelle

106

Ich bin gerade einem Projekt mit einer ziemlich großen vorhandenen Codebasis beigetreten. Wir entwickeln unter Linux und verwenden keine IDE. Wir laufen durch die Kommandozeile. Ich versuche herauszufinden, wie Python dazu gebracht wird, nach dem richtigen Pfad zu suchen, wenn ich Projektmodule ausführe. Zum Beispiel, wenn ich so etwas wie:

python someprojectfile.py

Ich bekomme

ImportError: no module named core.'somemodule'

Ich bekomme dies für alle meine Importe, um anzunehmen, dass es ein Problem mit dem Pfad ist.

TLDR:

Wie bringe ich Python dazu, ~/codez/project/während der Importanweisungen alle Dateien und Ordner nach * .py-Dateien zu durchsuchen ?

Der Maestro
quelle

Antworten:

171

Es gibt einige Möglichkeiten, dies zu tun:

  • Setzen Sie die Umgebungsvariable PYTHONPATHauf eine durch Doppelpunkte getrennte Liste von Verzeichnissen, um nach importierten Modulen zu suchen.
  • Fügen Sie in Ihrem Programm sys.path.append('/path/to/search')die Namen der Verzeichnisse hinzu, in denen Python nach importierten Modulen suchen soll. sys.pathist nur die Liste der Verzeichnisse, die Python jedes Mal durchsucht, wenn es aufgefordert wird, ein Modul zu importieren, und Sie können es nach Bedarf ändern (obwohl ich nicht empfehlen würde, eines der Standardverzeichnisse zu entfernen!). Alle Verzeichnisse, die Sie in die Umgebungsvariable PYTHONPATHeinfügen, werden sys.pathbeim Start von Python eingefügt .
  • Verwenden Sie site.addsitedirdiese Option , um ein Verzeichnis hinzuzufügen sys.path. Der Unterschied zwischen diesem und dem einfachen Anhängen besteht darin, dass bei der Verwendung addsitedirauch nach .pthDateien in diesem Verzeichnis gesucht wird und diese verwendet werden, um möglicherweise zusätzliche Verzeichnisse sys.pathbasierend auf dem Inhalt der Dateien hinzuzufügen . Weitere Informationen finden Sie in der Dokumentation.

Welche davon Sie verwenden möchten, hängt von Ihrer Situation ab. Denken Sie daran, dass wenn Sie Ihr Projekt an andere Benutzer verteilen, diese es normalerweise so installieren, dass die Python-Codedateien vom Python-Importer automatisch erkannt werden (dh Pakete werden normalerweise im site-packagesVerzeichnis installiert ), wenn Sie also mit sys.pathIhrem Code herumspielen Dies kann unnötig sein und sogar negative Auswirkungen haben, wenn dieser Code auf einem anderen Computer ausgeführt wird. Für die Entwicklung würde ich vermuten, dass die Einstellung PYTHONPATHnormalerweise der beste Weg ist.

Wenn Sie jedoch etwas verwenden, das nur auf Ihrem eigenen Computer ausgeführt wird (oder wenn Sie nicht standardmäßige Setups haben, z. B. manchmal in Web-App-Frameworks), ist es nicht ganz ungewöhnlich, so etwas zu tun

import sys
from os.path import dirname
sys.path.append(dirname(__file__))
David Z.
quelle
Wenn ich also 15 Unterverzeichnisse hätte, müsste ich jedes einzeln hinzufügen?
Themaestro
und könnten Sie ein Beispiel für ein Befehlszeilenargument geben, um PYTHONPATH zu ändern?
Themaestro
3
So legen Sie fest PYTHONPATH: In .bashrcoder eine Startdatei, die Ihre Shell verwendet (wenn es sich nicht um Bash handelt), schreiben Sie export PYTHONPATH=$PYTHONPATH:$HOME/codez/project. Aber wenn Sie eine Reihe von Unterverzeichnissen haben, würde ich eine .pthDatei erstellen und verwenden site.addsitedir. Sie können ein Modul erstellen sitecustomize, das die Funktion für Sie aufrufen kann. Versuchen Sie es auf ~/.local/lib/python2.6/sitecustomize.py(ersetzen Sie Ihre Python-Version), damit es hoffentlich automatisch importiert wird.
David Z
Ich habe Folgendes in meine .bashrc-Datei eingefügt und habe immer noch kein Glück mit diesen Importen. Irgendwelche Ideen? Wie würde ich überhaupt eine .pth-Datei erstellen? exportiere PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / exportiere PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / projekt exportiere PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / projectHAT $ HOME / adaifotis / codez / project / proxies exportieren PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / project / conf
themaestro
Versuchen Sie, ein Terminal zu öffnen und zu starten echo $PYTHONPATH. Wenn die Umgebungsvariable richtig festgelegt wurde, sollte eine durch Doppelpunkte getrennte Liste von Verzeichnissen angezeigt werden. Informationen zu .pthDateien finden Sie in der Dokumentation des siteModuls, auf das ich in meiner Antwort verwiesen habe. Hier erfahren Sie, wie der Inhalt aussehen soll und wie Sie ihn verwenden sollen.
David Z
13

Informationen zu Python-Paketen finden Sie auch hier: http://docs.python.org/tutorial/modules.html .

Aus Ihrem Beispiel würde ich vermuten, dass Sie wirklich ein Paket bei haben ~/codez/project. Die Datei __init__.pyin einem Python-Verzeichnis ordnet ein Verzeichnis einem Namespace zu. Wenn Ihre Unterverzeichnisse alle eine __init__.pyDatei haben, müssen Sie nur das Basisverzeichnis zu Ihrem hinzufügen PYTHONPATH. Beispielsweise:

PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / project

Zusätzlich zum Testen Ihrer PYTHONPATH-Umgebungsvariablen, wie David erklärt, können Sie sie in Python wie folgt testen:

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

...

Andrew B.
quelle
Vielleicht möchten Sie Ihre __init__.pyBackticks einfügen, um klar zu machen, dass Sie es wirklich meinen __init__.py, im Gegensatz zu init.py. Nur um verwirrende Neulinge zu vermeiden. :)
antred
4

Ich weiß, dass dieser Thread ein bisschen alt ist, aber ich habe einige Zeit gebraucht, um auf den Punkt zu kommen, also wollte ich ihn teilen.

In meinem Projekt hatte ich das Hauptskript in einem übergeordneten Verzeichnis und zur Unterscheidung der Module habe ich alle unterstützenden Module in einem Unterordner namens "modules" abgelegt. In meinem Hauptskript importiere ich diese Module wie folgt (für ein Modul namens report.py):

from modules.report import report, reportError

Wenn ich mein Hauptskript aufrufe, funktioniert dies. Ich wollte jedoch jedes Modul testen, indem ich ein main()in jedes einfügte und jedes direkt aufrief, als:

python modules/report.py

Jetzt beschwert sich Python, dass es "ein Modul namens Module" nicht finden kann. Der Schlüssel hier ist, dass Python standardmäßig den Ordner des Skripts in seinen Suchpfad einfügt, ABER NICHT DAS CWD. Dieser Fehler besagt also wirklich "Ich kann keinen Modul-Unterordner finden". Dies liegt daran, dass es kein Unterverzeichnis "modules" aus dem Verzeichnis gibt, in dem sich das Modul report.py befindet.

Ich finde, dass die beste Lösung dafür darin besteht, das CWD im Python-Suchpfad anzuhängen, indem Sie dies oben einfügen:

import sys

sys.path.append(".")

Jetzt durchsucht Python das CWD (aktuelles Verzeichnis), findet den Unterordner "modules" und alles ist in Ordnung.

Tom Gordon
quelle
3

Ich las diese Frage auf der Suche nach einer Antwort und mochte keine von ihnen.

Also habe ich eine schnelle und schmutzige Lösung geschrieben. Fügen Sie dies einfach irgendwo in Ihren sys.path ein und es wird ein beliebiges Verzeichnis unter folder(aus dem aktuellen Arbeitsverzeichnis) oder unter 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
]

Und ich mag es, weil ich einen Ordner für einige zufällige Tools haben kann und sie nicht Teil von Paketen oder irgendetwas sein kann und trotzdem in ein paar Codezeilen auf einige (oder alle) zugreifen kann.

Droogans
quelle
3

Der einfachste Weg, den ich finde, besteht darin, eine Datei "any_name.pth" zu erstellen und sie in Ihrem Ordner "\ Lib \ site-packages" abzulegen. Sie sollten diesen Ordner überall dort finden, wo Python installiert ist.

Fügen Sie in diese Datei eine Liste der Verzeichnisse ein, in denen Sie Module für den Import aufbewahren möchten. Machen Sie zum Beispiel eine Zeile in dieser Datei wie folgt:

C: \ Benutzer \ Beispiel ... \ Beispiel

Sie können feststellen, dass es funktioniert, indem Sie dies in Python ausführen:

import sys
for line in sys: print line

Sie sehen Ihr Verzeichnis unter anderem ausgedruckt, von wo aus Sie es auch importieren können. Jetzt können Sie eine "mymodule.py" -Datei importieren, die sich in diesem Verzeichnis so einfach befindet wie:

import mymodule

Dadurch werden keine Unterordner importiert. Dazu können Sie sich vorstellen, ein Python-Skript zu erstellen, um eine .pth-Datei zu erstellen, die alle Unterordner eines von Ihnen definierten Ordners enthält. Habe es vielleicht beim Start laufen lassen.

KieranPC
quelle
0

Neue Option für alte Frage.
Das Installieren des fail2banPakets unter Debian /usr/lib/python3/dist-packages/fail2banscheint fest codiert zu sein, um es auf einem Pfad zu installieren, der nicht auf Python3 ausgeführt wird 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']
>>>

Anstatt nur zu kopieren, habe ich die Bibliothek mit neueren Versionen verknüpft.
Zukünftige Updates der ursprünglichen App werden auch automatisch auf die verknüpften Versionen angewendet.

 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
quelle