Ist es sicher, ImportError abzufangen, wenn versucht wird, optionale Module zu importieren?

10

Normalerweise sehe ich dieses Muster mindestens einmal in jedem Python-Projekt, an dem ich arbeite. In einem Django-Projekt wird dies beispielsweise häufig am Ende der Basiseinstellungsdatei hinzugefügt:

try:
  from .local_settings import *
except ImportError:
  pass

Ebenfalls:

try:
  import simplejson as json
except ImportError:
  import json

Das hat mich aber immer ein bisschen gestört; Was ist, wenn das Modul erfolgreich importiert wurde, aber dann ein Selbst auslöst ImportError? Im ersten Beispiel ist das local_settingsModul beispielsweise vorhanden, local_settingsversucht dann jedoch , ein nicht vorhandenes Modul zu importieren.

Ist dies der sicherste Weg, ein optionales Modul zu importieren, gibt es einen besseren Weg, um diese Funktionalität zu erreichen, oder hängt es vom Kontext / der Verwendung ab (und wenn ja, welche Richtlinien gelten für die Entscheidung, wann dieser Ansatz verwendet werden soll)?


quelle

Antworten:

10

Es sollte allgemein angenommen werden, dass die optionale Abhängigkeit, die Sie importieren, selbstständig funktionieren kann. Es gibt kaum einen Unterschied zwischen dem Versuch, eine fehlende optionale Abhängigkeit zu importieren, und einer, die nicht importiert werden kann, da eine erforderliche transitive Abhängigkeit fehlt.

Mit anderen Worten, warum sollte sich Ihr Programm darum kümmern müssen, simplejsonnicht verfügbar zu sein, weil es nicht installiert ist oder weil eine Abhängigkeit von simplejsonnicht installiert ist? So oder so können Sie nicht verwenden simplejson.

Bei optionalen Abhängigkeiten muss das Paketinstallationsprogramm sicherstellen, dass eine Abhängigkeit korrekt installiert ist, einschließlich der transitiven Abhängigkeiten.

Für so etwas local_settingsbesteht in der Tat ein (kleines) Risiko, dass ein Transitiv ImportErrormaskiert wird. Sie können die abgefangene ImportErrorAusnahme jederzeit auf Stufe DEBUG oder ähnlich protokollieren , um leichter überprüfen zu können, was die Ausnahme verursacht haben könnte:

try:
    from .local_settings import *
except ImportError:
    log.debug('local_settings failed to import', exc_info=True)

Das loggingPaket enthält die Ausnahmeinformationen für eine spätere Überprüfung im Protokoll, wenn Sie exc_infoauf true setzen.

Eine andere Möglichkeit besteht darin, eine Warnung mit dem warningsModul auszugeben . Die Standardbibliothek hat eine ImportWarningKlasse dafür:

import warnings

try:
    from .local_settings import *
except ImportError:
    warnings.warn('local_settings failed to import', ImportWarning)
Martijn Pieters
quelle
Ich schlage vor, ImportWarning zu verwenden, das in offiziellen Dokumenten neben oder anstelle der Protokollierung erwähnt wird.
Eray Erdin
1
@ErayErdin: Das hängt vom Anwendungsfall ab. Wenn das Modul als Leistungsoptimierung importiert wird, können Sie argumentieren, dass eine Warnung nur Rauschen ist. Dies ist sicherlich kein Fehler, wie in der Dokumentation vorgeschlagen ImportWarning.
Martijn Pieters
3

Dies ist im Allgemeinen sicher, vorausgesetzt, Ihre Module sind frei von Nebenwirkungen während des Imports.

Wenn ein importiertes Modul ImportErrorbeim Import (nicht nur ) eine Ausnahme auslöst, wird es aus entferntsys.modules . Das heißt, wenn ein anderer Code versucht, das Modul zu importieren, erhält er kein teilweise initialisiertes Modul. Stattdessen versucht Python, das Modul erneut zu laden, und schlägt vermutlich erneut fehl ImportError.

Dies kann zu Problemen führen, wenn Ihre Module Nebenwirkungen beim Import haben, da diese Effekte nicht rückgängig gemacht werden. Insbesondere wenn das fehlerhafte Modul ein anderes Modul erfolgreich importiert, wird dieses nicht entfernt sys.modules. Diese besondere Nebenwirkung ist selten ein Problem, aber einige Nebenwirkungen können problematischer sein.

Kevin
quelle