Ich versuche, PEP 328 mit der folgenden Verzeichnisstruktur zu folgen:
pkg/
__init__.py
components/
core.py
__init__.py
tests/
core_test.py
__init__.py
In core_test.py
habe ich die folgende Importanweisung
from ..components.core import GameLoopEvents
Beim Ausführen erhalte ich jedoch folgende Fehlermeldung:
tests$ python core_test.py
Traceback (most recent call last):
File "core_test.py", line 3, in <module>
from ..components.core import GameLoopEvents
ValueError: Attempted relative import in non-package
Ich habe gefunden, dass " relativer Pfad nicht funktioniert, obwohl __init__.py " und " Importieren eines Moduls aus einem relativen Pfad " aber sie haben nicht geholfen.
Fehlt hier etwas?
Ja. Sie verwenden es nicht als Paket.
python -m pkg.tests.core_test
Zu erläutern auf Ignacio Vazquez-Abrams Antwort:
Der Python-Importmechanismus funktioniert relativ zum __name__
der aktuellen Datei. Wenn Sie eine Datei direkt ausführen, hat sie nicht ihren üblichen Namen, sondern "__main__"
als Namen. Relative Importe funktionieren also nicht.
Sie können es, wie von Igancio vorgeschlagen, mit der Option -m
ausführen. Wenn Sie einen Teil Ihres Pakets haben, der als Skript ausgeführt werden soll, können Sie das Attribut __package__
verwenden, um dieser Datei mitzuteilen, welchen Namen sie in der Pakethierarchie haben soll.
Weitere Informationen finden Sie unter http://www.python.org/dev/peps/pep-0366/ .
Sie können import components.core
direkt verwenden, wenn Sie das aktuelle Verzeichnis an sys.path
anhängen:
if __== '__main__' and __package__ is None:
from os import sys, path
sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
Es hängt davon ab, wie Sie Ihr Skript starten möchten.
Wenn Sie Ihren UnitTest von der Kommandozeile aus starten möchten auf klassische Weise, heißt das:
python tests/core_test.py
Da in diesem Fall 'components' und 'tests' gleichgeordnete Ordner sind, können Sie das relative Modul entweder mit insert oder mit append Methode des sys.path Moduls . Etwas wie:
import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents
Andernfalls können Sie Ihr Skript mit dem Argument '-m' starten (Beachten Sie, dass wir in diesem Fall von einem Paket sprechen und daher die Erweiterung '.py'nicht angeben dürfen ), das ist:
python -m pkg.tests.core_test
In diesem Fall können Sie den relativen Import einfach wie gewohnt verwenden:
from ..components.core import GameLoopEvents
Sie können die beiden Ansätze schließlich mischen, so dass Ihr Skript unabhängig von der Art des Aufrufs funktioniert.
if __== '__main__':
if __package__ is None:
import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents
else:
from ..components.core import GameLoopEvents
In core_test.py wie folgt vorgehen:
import sys
sys.path.append('../components')
from core import GameLoopEvents
Wenn Ihr Anwendungsfall für das Ausführen von Tests bestimmt ist und es den Anschein hat, dass dies der Fall ist, können Sie Folgendes tun. Anstatt Ihr Testskript als python core_test.py
auszuführen, verwenden Sie ein Testframework wie pytest
. Dann können Sie in der Kommandozeile eingeben
$$ py.test
Dadurch werden die Tests in Ihrem Verzeichnis ausgeführt. Dies umgeht das Problem von __name__
, das __main__
ist, auf das @BrenBarn hingewiesen hat. Fügen Sie als nächstes eine leere __init__.py
-Datei in Ihr Testverzeichnis ein. Dadurch wird das Testverzeichnis Teil Ihres Pakets. Dann können Sie es tun
from ..components.core import GameLoopEvents
Wenn Sie jedoch Ihr Testskript als Hauptprogramm ausführen, schlagen die Dinge erneut fehl. Verwenden Sie also einfach den Testläufer. Vielleicht funktioniert das auch mit anderen Testläufern wie nosetests
, aber ich habe es nicht geprüft. Hoffe das hilft.
Mein Quick-Fix besteht darin, das Verzeichnis dem Pfad hinzuzufügen:
import sys
sys.path.insert(0, '../components/')
Alter Faden. Ich fand heraus, dass das Hinzufügen eines __all__= ['submodule', ...]
zur __init__.py -Datei und dann die Verwendung des from <CURRENT_MODULE> import *
im Ziel gut funktioniert.
Dieser Ansatz hat bei mir funktioniert und ist weniger überladen als einige Lösungen:
try:
from ..components.core import GameLoopEvents
except ValueError:
from components.core import GameLoopEvents
Das übergeordnete Verzeichnis befindet sich in meinem PYTHONPATH, und im übergeordneten Verzeichnis und in diesem Verzeichnis befinden sich __init__.py
-Dateien.
Versuche dies
import components
from components import *
Wenn jemand nach einem Workaround sucht, bin ich auf einen gestoßen. Hier ist ein bisschen Kontext. Ich wollte eine der Methoden testen, die ich in einer Datei habe. Wenn ich es von innen laufen lasse
if __== "__main__":
es klagte immer über die relativen Importe. Ich habe versucht, die obigen Lösungen anzuwenden, konnte jedoch nicht funktionieren, da es viele geschachtelte Dateien mit jeweils mehreren Importen gab.
Hier ist was ich getan habe. Ich habe gerade einen Launcher erstellt, ein externes Programm, das notwendige Methoden importieren und aufrufen würde. Obwohl es keine gute Lösung ist, funktioniert es.
Wie Paolo gesagt hat, haben wir zwei Aufrufmethoden:
1) python -m tests.core_test
2) python tests/core_test.py
Ein Unterschied zwischen ihnen ist sys.path [0] string. Da der Interpret durchsucht sys.path beim Importieren , können wir tests/core_test.py
verwenden:
if __== '__main__':
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from components import core
<other stuff>
Und danach können wir core_test.py mit anderen Methoden ausführen:
cd tests
python core_test.py
python -m core_test
...
Beachten Sie, dass nur py36 getestet wurde.