Ich versuche mein Projekt für die Verteilung zu packen, aber ich treffe RuntimeWarning
, wenn ich das Modul starte.
Ich habe einen Fehlerbericht in der Python-Mailingliste gefunden, der anzeigt, dass das RuntimeWarning
neues Verhalten ist, das eingeführt wurde in Python 3.5.2.
Beim Lesen des Fehlerberichts scheint ein Doppelimport zu erfolgen, und dieser RuntimeWarning
ist korrekt, um den Benutzer zu alarmieren. Ich sehe jedoch nicht, welche Änderungen ich an meiner eigenen Projektstruktur vornehmen muss, um dieses Problem zu vermeiden.
Dies ist das erste Projekt, das ich "richtig" strukturieren wollte. Ich hätte gerne ein ordentliches Layout, wenn ich den Code drücke, und eine Projektstruktur, die geklont und von anderen einfach ausgeführt werden kann.
Ich habe meine Struktur hauptsächlich auf Ссылка aufgebaut.
Ich habe Details eines Mindestarbeitsbeispiels unten hinzugefügt.
Um das Problem zu replizieren, führe ich die Hauptdatei mit python -m
:
Das Ausführen meiner Tests ist in Ordnung:
%Vor%Eine Projektstruktur zur Replizierung des Problems lautet wie folgt:
%Vor% In der Datei proj/proj.py
:
In proj/__init__.py
:
In tests/context.py
:
Schließlich in tests/test_proj.py
:
Kann mir jemand helfen, meine Projektstruktur zu korrigieren, um dieses Doppelimport-Szenario zu vermeiden? Jede Hilfe mit diesem würde sehr geschätzt werden.
In diesem speziellen Fall liegt die doppelte Importwarnung an dieser Zeile in proj/__init__.py
:
Was diese Zeile bedeutet ist, dass zu dem Zeitpunkt, zu dem die -m
switch-Implementierung den import proj
-Schritt beendet, proj.proj
bereits bereits importiert wurde / ist als Nebeneffekt des Importierens des übergeordneten Pakets.
Vermeidung der Warnung
Um die Warnung zu vermeiden, müssen Sie eine Möglichkeit finden, sicherzustellen, dass das importierte Paket nicht implizit das Paket importiert, das mit dem Schalter -m
ausgeführt wird.
Die beiden Hauptoptionen zum Lösen sind:
from .proj import main
(wie @John Moutafis vorgeschlagen), vorausgesetzt, dass dies ohne Verletzung der API-Kompatibilitätsgarantien erfolgen kann; oder Löschen Sie den if __name__ == "__main__":
-Block aus dem proj
Submodul und ersetzen Sie ihn durch eine separate proj/__main__.py
-Datei, die einfach funktioniert:
Wenn Sie mit Option 2 fortfahren, ändert sich auch der Aufruf der Befehlszeile so, dass er nur python -m proj
lautet, anstatt auf ein Submodul zu verweisen.
Eine stärker abwärtskompatible Variante von Option 2 ist das Hinzufügen von __main__.py
, ohne den CLI-Block aus dem aktuellen Submodul zu löschen. Dies kann besonders in Verbindung mit DeprecationWarning
:
Wenn proj/__main__.py
bereits für einen anderen Zweck verwendet wird, können Sie auch python -m proj.proj
durch python -m proj.proj_cli
ersetzen, wobei proj/proj_cli.py
folgendermaßen aussieht:
Warum existiert die Warnung?
Diese Warnung wird ausgegeben, wenn die -m
-Schalterimplementierung im Begriff ist, den Code eines bereits importierten Moduls erneut zu laden im Modul __main__
, was bedeutet, dass Sie zwei verschiedene Kopien von allem haben werden Es definiert - Klassen, Funktionen, Container, etc.
Je nach den Besonderheiten der Anwendung kann dies gut funktionieren (weshalb es eher eine Warnung als ein Fehler ist), oder es kann zu bizarrem Verhalten führen, wie z. B. Statusänderungen auf Modulebene nicht wie erwartet oder sogar Ausnahmen nicht gefangen werden, weil der Ausnahmebehandler versucht hat, den Ausnahmetyp von einer Instanz des Moduls abzufangen, während die Ausnahme ausgelöst den Typ von der anderen Instanz verwendet hat.
Daher die vage this may cause unpredictable behaviour
Warnung - wenn die Dinge schief laufen, weil der Code der obersten Ebene des Moduls zweimal ausgeführt wird, können die Symptome so ziemlich alles sein.
Wie können Sie komplexere Fälle debuggen?
Während in diesem speziellen Beispiel der Nebeneffekt-Import direkt in proj/__init__.py
ist, gibt es eine viel subtilere und schwer zu debuggende Variante, wo das übergeordnete Paket stattdessen:
und dann ist some_other_module
(oder ein importiertes Modul):
Unter der Annahme, dass das Fehlverhalten reproduzierbar ist, besteht der Hauptweg zum Debuggen dieser Art von Problemen darin, Python im ausführlichen Modus auszuführen und die Importsequenz zu überprüfen:
%Vor% Dieses spezielle Beispiel zeigt nur die Basis von Importen, die Python 2.7 auf Fedora beim Start ausführt. Wenn Sie einen Doppelimport RuntimeWarning
wie den in dieser Frage debuggen, würden Sie in der ausführlichen Ausgabe nach den Zeilen "import proj" und dann "import proj.proj" suchen und dann die unmittelbar vorhergehenden Importe genau betrachten die Zeile "import proj.proj".
Wenn Sie sich die Doppelimport-Trap
Dieser nächste Trap existiert in allen aktuellen Versionen von Python, einschließlich 3.3, und kann in der folgenden allgemeinen Richtlinie zusammengefasst werden: "Fügen Sie niemals direkt ein Paketverzeichnis oder ein beliebiges Verzeichnis in einem Paket hinzu zum Python-Pfad ".
Der Grund dafür ist, dass jedes Modul in diesem Verzeichnis problematisch ist ist jetzt potenziell unter zwei verschiedenen Namen zugänglich: als Top Level-Modul (da das Verzeichnis auf sys.path ist) und als Submodul des Pakets (wenn das Verzeichnis der höheren Ebene das Paket enthält) selbst ist auch auf sys.path).
In tests/context.py
entfernen: sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
was wahrscheinlich das Problem verursacht und Ihr Code funktioniert immer noch wie erwartet.
Bearbeiten aufgrund eines Kommentars:
Sie können versuchen, einige Teile in Ihrem Code zu ändern:
proj/__init__.py
Kann komplett leer sein Am test_proj.py
sollte die Importe wie folgt ändern:
PS: Ich konnte die Warnung unter Linux weder mit Ihrem ursprünglichen Code noch mit meinen Vorschlägen reproduzieren.
python -m ist ein bisschen schwierig. @ncoghlan hat bereits detaillierte Informationen zur Verfügung gestellt. Wenn wir versuchen, mit python -m zu laufen, werden standardmäßig alle Pakete in sys.path / pythonpath importiert. Wenn Ihr Paket eine Anweisung import in die Verzeichnisse in den PATHs hat, tritt die obige Warnung auf.
Mein PYTHONPATH hat bereits das Projektverzeichnis. Also wenn ich das mache
%Vor%System gibt die Warnung aus. Daher müssen keine expliziten Importe vorgenommen werden, wenn sich der Pfad im Python-Pfad befindet.
Wenn Sie sicher sind, dass die Warnung für Sie nicht relevant ist, können Sie RuntimeWarnings einfach durch das Modul runpy
ignorieren, das die Logik hinter dem Schalter -m
implementiert:
Dies kann natürlich auch relevante Warnungen verbergen, aber zumindest im Moment ist dies das einzige RuntimeWarning, das runpy
verwendet. Alternativ könnte die Filterung strenger gemacht werden, indem ein Muster für die Nachrichten- oder Zeilennummer spezifiziert wird, wo eine Warnung auftreten muss, aber beide können unterbrochen werden, wenn runpy
später bearbeitet wird.
Tags und Links python python-3.x