it-swarm.com.de

Behebung des Problems "Versuchter relativer Import in Nichtpaket", auch mit __init__.py

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?

651
skytreader

Ja. Sie verwenden es nicht als Paket.

python -m pkg.tests.core_test
389

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/ .

580
BrenBarn

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__))))
197
ihm

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
165
Paolo Rovelli

In core_test.py wie folgt vorgehen:

import sys
sys.path.append('../components')
from core import GameLoopEvents
16
Allan Mwesigwa

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. 

9
deepak

Mein Quick-Fix besteht darin, das Verzeichnis dem Pfad hinzuzufügen:

import sys
sys.path.insert(0, '../components/')
7
v4gil

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.

2
Laurent

Sie können from pkg.components.core import GameLoopEvents verwenden, zum Beispiel verwende ich pycharm. Das Bild unten ist mein Projektstruktur-Image. Ich importiere es einfach aus dem Root-Paket.

 enter image description here

1
Jayhello

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.

0
Rick Graves

Versuche dies

import components
from components import *
0
Vaishnavi Bala

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.

0
HappyWaters

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.

0
zhengcao