PEP8 - Import nicht oben in der Datei mit sys.path

75

Problem

PEP8 hat eine Regel zum Einfügen von Importen an den Anfang einer Datei:

Importe werden immer am Anfang der Datei platziert, direkt nach Modulkommentaren und Dokumentzeichenfolgen sowie vor Modulglobalen und -konstanten.

In bestimmten Fällen möchte ich jedoch möglicherweise Folgendes tun:

import sys
sys.path.insert("..", 0)

import my_module

In diesem Fall kennzeichnet das pep8Befehlszeilenprogramm meinen Code:

Import auf E402-Modulebene nicht oben in der Datei

Was ist der beste Weg, um die PEP8-Konformität mit sys.pathÄnderungen zu erreichen ?

Warum

Ich habe diesen Code, weil ich der Projektstruktur folge, die im Per Anhalter durch Python angegeben ist .

In diesem Handbuch wird vorgeschlagen, dass ich einen my_moduleOrdner habe, der von einem testsOrdner getrennt ist und sich beide im selben Verzeichnis befinden. Wenn ich zugreifen möchten my_moduleaus tests, ich glaube , ich muss hinzufügen , ..um diesys.path

Luke Taylor
quelle
Warum schreibst du nicht eine setup.pyund installierst sie tatsächlich my_modulezum Testen?
Jonrsharpe
Weil das etwas weniger bequem ist. Ich denke ich könnte, aber ich möchte lieber nicht.
Luke Taylor
Für wen? Wenn Sie jemals wirklich wollen verwenden dieses Projekt überall ist es bei weitem der einfachste Weg , um es zu bekommen und läuft.
Jonrsharpe
9
Aus PEP8 : "Wissen Sie jedoch, wann Sie inkonsistent sein müssen - manchmal sind Empfehlungen für Styleguides einfach nicht anwendbar. Verwenden Sie im Zweifelsfall Ihr bestes Urteilsvermögen." Es gibt Zeiten, in denen Sie die PEP8-Konformität brechen müssen, und das ist in Ordnung.
SethMMorton
1
@jonrsharpe Es ist eine gute Angewohnheit, sich auf zukünftige Dinge einzulassen, die ich teilen werde. (Ich verstehe Ihren Standpunkt jedoch, in diesem Fall kann ich setup.py verwenden). Ich werde mir das merken.
Luke Taylor

Antworten:

60

Oft habe ich mehrere Dateien mit Tests in einem Unterverzeichnis foo/testsmeines Projekts, während sich die Module, in denen ich teste, befinden foo/src. Um die Tests foo/testsohne Importfehler auszuführen, erstelle ich eine Datei foo/tests/pathmagic.py, die so aussieht.

"""Path hack to make tests work."""

import os
import sys

bp = os.path.dirname(os.path.realpath('.')).split(os.sep)
modpath = os.sep.join(bp + ['src'])
sys.path.insert(0, modpath)

In jeder Testdatei verwende ich dann

import pathmagic  # noqa

als erster Import. Die „noqa“ Kommentar verhindert pycodestyle/ pep8von etwa einem nicht verwendeten Import beschweren.

Roland Smith
quelle
2
Das ist cool, aber es gibt immer noch ein Problem mit "importiertem, aber nicht verwendetem [F401]".
Chung-Yen Hung
2
Ich denke, eine Dummy-Funktion in diesem pathmagischen Modul zu erstellen und vom Testmodul aus aufzurufen, würde dieses Problem lösen, aber ich wünschte, es gäbe etwas
Saubereres
3
@ Chung-YenHung Beachten Sie, dass Pycodestyle / Pep8-Warnungen eher als Hinweis als als Syntaxfehler oder Ausnahmen dienen. Sie können sie ignorieren. Ich habe meine Antwort aktualisiert, indem ich nach dem Import einen "noqa" -Kommentar hinzugefügt habe.
Roland Smith
Ein großer Nachteil dabei ist, dass Ihre Tests in ein Paket umgewandelt pathmagicwerden, das importiert werden kann. Die meisten Python-Testläufer gehen davon aus, dass es sich bei Ihren Tests um eine Sammlung von Dateien handelt, die nicht aktiviert sind. Eine sys.pathÄnderung, die zu Problemen führen kann, zeigt, wie pytestmit diesen Problemen umgegangen wird. Docs.pytest.org/en/latest/…
Meitham
1
@Meitham Obwohl in meiner Antwort erwähnt wird, wie ich dies damals zum Ausführen von Tests verwendet habe, geht es bei der Frage nicht darum, Tests auszuführen. (Ich bin seitdem für meine Tests zum Pytest übergegangen.) In anderen Situationen ist dieser Mechanismus immer noch nützlich.
Roland Smith
80

Wenn es nur wenige Importe gibt, können Sie PEP8 in diesen importZeilen einfach ignorieren :

import sys
sys.path.insert("..", 0)
import my_module  # noqa: E402
Astorga
quelle
6
Ich ziehe es vor, expliziter zu sein und die verletzte Regel wie # noqa: E402zum Beispiel anzugeben . ( Quelle )
Max Goodridge
@ MaxGoodridge in der Tat! Antwort bearbeitet, um die Regel hinzuzufügen.
Astorga
10

Es gibt noch eine andere Problemumgehung.

import sys
... all your other imports...

sys.path.insert("..", 0)
try:
    import my_module
except:
    raise
Peuchele
quelle
1
Ich bin fest davon überzeugt, dass dies nicht funktionieren wird. Der Grund dafür ist, dass unter "... all Ihre Importe ..." möglicherweise einige Module importiert werden, für die möglicherweise zuerst PYTHONPATH festgelegt werden muss.
darkdefender27
2
@ darkdefender27 Die Idee ist, alle Importe, die PYTHONPATH erfordern, in den tryKörper und alles andere (was nicht davon abhängt) oben zu setzen.
Astorga
oder noch einfacher:if 1: import module
stason
4

Ich habe gerade mit einer ähnlichen Frage gekämpft und ich denke, ich habe eine etwas schönere Lösung gefunden als die akzeptierte Antwort.

Erstellen Sie ein pathmagicModul, das die eigentliche Manipulation von sys.path ausführt, die Änderung jedoch in einem Kontextmanager vornimmt :

"""Path hack to make tests work."""

import os
import sys

class context:
    def __enter__(self):
        bp = os.path.dirname(os.path.realpath('.')).split(os.sep)
        modpath = os.sep.join(bp + ['src'])
        sys.path.insert(0, modpath)

    def __exit__(self, *args):
        pass

Dann tun Sie in Ihren Testdateien (oder wo immer Sie dies benötigen):

import pathmagic

with pathmagic.context():
    import my_module
    # ...

Auf diese Weise erhalten Sie keine Beschwerden von flake8 / pycodestyle, Sie benötigen keine speziellen Kommentare und die Struktur scheint sinnvoll zu sein.

Erwägen Sie für zusätzliche Sauberkeit, den Pfad im __exit__Block tatsächlich zurückzusetzen. Dies kann jedoch zu Problemen bei verzögerten Importen führen (wenn Sie den Modulcode außerhalb des Kontexts platzieren), sodass sich die Mühe möglicherweise nicht lohnt.


BEARBEITEN : Ich habe gerade einen viel einfacheren Trick bei der Beantwortung einer anderen Frage gesehen : Fügen Sie assert pathmagicunter Ihren Importen hinzu, um den noqaKommentar zu vermeiden .

itsadok
quelle
Alles, was dies wirklich erreicht hat, ist, den nächsten für den speziellen Kommentar loszuwerden, auf Kosten der Verwendung eines Kontextmanagers - ein meiner Meinung nach etwas nebulöser Kompromiss. Um das Bereinigen im __exit__Block wirklich ordnungsgemäß durchzuführen, müsste lediglich der hinzugefügte Pfad entfernt werden (sofern er noch vorhanden ist), da durch Wiederherstellen des gesamten vorherigen Werts auf den Stand bei Eingabe des Kontexts auch andere Änderungen rückgängig gemacht werden könnten wurden aus irgendeinem Grund von einem anderen Code (der im selben Kontext ausgeführt wird) dazu gebracht.
Martineau
@martineau in der Tat, es ist eine Frage des Geschmacks. Ich bin möglicherweise ein wenig voreingenommen gegenüber speziellen Kommentaren, da meine aktuelle Codebasis etwas zu viele Kommentare für die unzähligen statischen Analysetools und Editoren enthält, die die verschiedenen Teams verwenden. Auch auf Ihren zweiten Punkt vereinbart.
Itsadok
Problematisch, wenn pathmagic.context()beliebig oft aufgerufen werden kann (zB wenn alle Tests ausgeführt werden). Das Zurücksetzen ist ebenfalls problematisch: Jeder spätere Import (z. B. bei Bedarf) kann fehlschlagen.
ivan_pozdeev
4

Haben Sie bereits Folgendes versucht:

import sys
from importlib import import_module

sys.path.insert("..", 0)

# import module
my_mod = import_module('my_module')

# get method or function from my_mod
my_method = getattr(my_mod , 'my_method')
Daniel Lee
quelle
2

Um pep8 zu erfüllen, sollten Sie Ihren Projektpfad in den Python-Pfad aufnehmen, um relative / absolute Importe durchzuführen.

Zu diesem Zweck können Sie sich diese Antwort ansehen: Fügen Sie PYTHONPATH dauerhaft ein Verzeichnis hinzu

Pierre Barre
quelle
3
Ich möchte dieses Verzeichnis nicht für alle meine Python-Skripte global zugänglich machen, da dies zu Konflikten führen kann.
Luke Taylor
2
Sie können das Paket docs.python.org/3/library/pkgutil.html verwenden , um Namespaces zu verwenden. Wenn Sie der Meinung sind, dass Ihre Lösung die beste ist, sind Sie nicht verpflichtet, pep8 zu befolgen. Pep8 ist nur ein Ratschlag und eine bewährte Methode. Das bedeutet nicht, dass Sie jede Regel jederzeit und überall befolgen müssen.
Pierre Barre