So überprüfen Sie, ob ein Python-Modul vorhanden ist, ohne es zu importieren

179

Ich muss wissen, ob ein Python-Modul vorhanden ist, ohne es zu importieren.

Importieren von etwas, das möglicherweise nicht vorhanden ist (nicht das, was ich möchte):

try:
    import eggs
except ImportError:
    pass
Yarbelk
quelle
4
Ich bin gespannt, was sind die Nachteile der Verwendung von Import?
Chuck
15
Wenn Ihr Modul Nebenwirkungen hat, kann das Aufrufen des Imports unerwünschte Folgen haben. Wenn Sie also überprüfen möchten, welche Version einer Datei zuerst ausgeführt werden soll, können Sie dies anhand der folgenden Antwort überprüfen und später importieren. Ich behaupte nicht, dass es eine gute Idee ist, Module mit Nebenwirkungen zu schreiben - aber wir sind alle Erwachsene und können unsere eigenen Entscheidungen darüber treffen, wie gefährlich wir codieren möchten.
Yarbelk
1
@ArtOfWarfare Ich habe gerade die Frage geschlossen , die Sie als Duplikat dieser Frage verlinkt haben . Weil diese Frage klarer ist und auch die hier vorgeschlagene Lösung besser ist als alle anderen dort aufgeführten. Ich möchte lieber darauf hinweisen, wer eine Antwort auf diese bessere Lösung haben möchte, als die Leute davon abzulenken.
Bakuriu
7
@Chuck Zusätzlich kann das Modul vorhanden sein, kann aber selbst Importfehler enthalten. Das Abfangen von ImportErrors wie im obigen Code kann dazu führen, dass das Modul nicht vorhanden ist, wenn dies tatsächlich der Fall ist, aber Fehler aufweist.
Michael Barton

Antworten:

198

Python2

Um zu überprüfen, ob der Import etwas in Python2 finden kann, verwenden Sie imp

import imp
try:
    imp.find_module('eggs')
    found = True
except ImportError:
    found = False

Um gepunktete Importe zu finden, müssen Sie mehr tun:

import imp
try:
    spam_info = imp.find_module('spam')
    spam = imp.load_module('spam', *spam_info)
    imp.find_module('eggs', spam.__path__) # __path__ is already a list
    found = True
except ImportError:
    found = False

Sie können auch verwenden pkgutil.find_loader(mehr oder weniger das gleiche wie der Python3-Teil

import pkgutil
eggs_loader = pkgutil.find_loader('eggs')
found = eggs_loader is not None

Python3

Python3 ≤ 3.3

Sie sollten verwenden importlib, wie ich dies tat, war:

import importlib
spam_loader = importlib.find_loader('spam')
found = spam_loader is not None

Meine Erwartung ist, wenn Sie einen Lader dafür finden können, dann existiert er. Sie können auch etwas schlauer sein, z. B. herausfinden, welche Lader Sie akzeptieren. Beispielsweise:

import importlib
spam_loader = importlib.find_loader('spam')
# only accept it as valid if there is a source file for the module - no bytecode only.
found = issubclass(type(spam_loader), importlib.machinery.SourceFileLoader)

Python3 ≥ 3.4

In Python3.4 wurden importlib.find_loader Python-Dokumente zugunsten von abgelehnt importlib.util.find_spec. Die empfohlene Methode ist die importlib.util.find_spec. Es gibt andere wie importlib.machinery.FileFinder, was nützlich ist, wenn Sie nach einer bestimmten Datei zum Laden suchen. Es liegt außerhalb des Rahmens, herauszufinden, wie man sie verwendet.

import importlib
spam_spec = importlib.util.find_spec("spam")
found = spam_spec is not None

Dies funktioniert auch bei relativen Importen, Sie müssen jedoch das Startpaket angeben, damit Sie auch Folgendes tun können:

import importlib
spam_spec = importlib.util.find_spec("..spam", package="eggs.bar")
found = spam_spec is not None
spam_spec.name == "eggs.spam"

Ich bin mir zwar sicher, dass es einen Grund dafür gibt - aber ich bin mir nicht sicher, was es sein würde.

WARNUNG

Beim Versuch, ein Submodul zu finden, wird das übergeordnete Modul importiert (für alle oben genannten Methoden)!

food/
  |- __init__.py
  |- eggs.py

## __init__.py
print("module food loaded")

## eggs.py
print("module eggs")

were you then to run
>>> import importlib
>>> spam_spec = importlib.find_spec("food.eggs")
module food loaded
ModuleSpec(name='food.eggs', loader=<_frozen_importlib.SourceFileLoader object at 0x10221df28>, origin='/home/user/food/eggs.py')

Kommentare sind willkommen, um dies zu umgehen

Danksagung

  • @rvighne für importlib
  • @ lucas-guido für python3.3 + berauben find_loader
  • @enpenax für das Verhalten von pkgutils.find_loader in python2.7
Yarbelk
quelle
3
Dies funktioniert nur für Module der obersten Ebene, nicht für eggs.ham.spam.
Hemflit
3
@hemflit , wenn Sie wissen wollen , spamin die eggs.hamSie verwenden würdenimp.find_module('spam', ['eggs', 'ham'])
gitaarik
5
1, aber impist veraltet zugunsten von importlibin Python 3.
rvighne
4
Was ist, wenn das importierte Modul einen tatsächlichen "ImportError" enthält? Das ist mir passiert. Dann existiert das Modul, wird aber nicht "gefunden".
Enpenax
1
Nach einem Jahr stieß ich auf das gleiche Problem, das ich gerade erwähnt hatte, und suchte nach einer Lösung für Python 2: pkgutil.find_loader("my.package.module")Gibt einen Loader zurück, wenn das Paket / Modul vorhanden ist und Nonewenn nicht. Bitte aktualisieren Sie Ihre Antwort für Python 2, da mich das Maskieren des ImportError gestern verrückt gemacht hat xP
enpenax
13

Nach der Verwendung von yarbelks Antwort habe ich dies gemacht, damit ich nicht importieren muss ìmp.

try:
    __import__('imp').find_module('eggs')
    # Make things with supposed existing module
except ImportError:
    pass

Nützlich settings.pyzum Beispiel bei Django .

Zulu-
quelle
4
Ich habe ein Downvoting durchgeführt, da dies Importfehler im Modul maskiert, was es wirklich schwierig macht, den Fehler zu erkennen.
Enpenax
1
Downvote ist eine schlechte Idee, eine gute Übung ist "immer gefangene Fehler protokollieren". Dies ist ein Beispiel, nachdem Sie geschrieben haben, wie Sie es wollen.
Zulu
2
Wie würden Sie einen Fehler protokollieren, wenn das importierte Modul in Zeile 1 mit einem ImportError fehlschlägt und Ihr Try-Catch dazu führt, dass es stillschweigend fehlschlägt?
Enpenax
Ich bin gerade auf das Problem der Maskierungs-Import-Fehler im wirklichen Leben gestoßen, und es war schlecht (was zu Tests führte, die nicht bestanden werden sollten!).
David gegeben
Ich bin auf das
gestoßen
13

Python 3> = 3.6: ModuleNotFoundError

Das ModuleNotFoundErrorwurde in Python 3.6 eingeführt und kann für diesen Zweck verwendet werden

try:
    import eggs
except ModuleNotFoundError:
    # Error handling
    pass

Der Fehler wird ausgelöst, wenn ein Modul oder eines seiner übergeordneten Elemente nicht gefunden werden kann. So

try:
    import eggs.sub
except ModuleNotFoundError as err:
    # Error handling
    print(err)

würde eine Nachricht drucken, die aussieht, No module named 'eggs'wenn das eggsModul nicht gefunden werden kann; würde aber so etwas drucken wie No module named 'eggs.sub'wenn nur das submodul nicht gefunden werden könnte aber daseggs Paket gefunden werden könnte.

Weitere Informationen finden Sie in der Dokumentation des ImportsystemsModuleNotFoundError

J. Baoby
quelle
1
Dies beantwortet die Frage nicht, da es das Paket importiert, falls es existiert
Divenex
11

Python 2, ohne sich auf ImportError zu verlassen

Bis die aktuelle Antwort aktualisiert ist, ist hier der Weg für Python 2

import pkgutil
import importlib

if pkgutil.find_loader(mod) is not None:
    return importlib.import_module(mod)
return None

Warum noch eine Antwort?

Viele Antworten nutzen das Fangen eines ImportError. Das Problem dabei ist, dass wir nicht wissen können, was das wirft ImportError.

Wenn Sie Ihr vorhandenes Modul importieren und es zufällig ein ImportErrorModul in Ihrem Modul gibt (z. B. Tippfehler in Zeile 1), führt dies dazu, dass Ihr Modul nicht vorhanden ist. Sie benötigen eine Menge Backtracking, um herauszufinden, dass Ihr Modul vorhanden ist und das ImportErrorabgefangen wird und die Dinge lautlos versagen.

Enpenax
quelle
Es mag unklar gewesen sein, aber alle außer den ersten Codeblöcken hängen nicht davon ab ImportError- bitte bearbeiten Sie, wenn es Ihnen unklar war.
Yarbelk
Ich sehe, dass Sie den ImportError-Fang in den ersten beiden Python 2-Beispielen verwenden. Warum sind sie dann dort?
Enpenax
3
Dies löst ImportError aus, wenn mod == 'not_existing_package.mymodule'. Siehe meine vollständige Lösung unten
Marcin Raczyński
1
Natürlich wird ein Importfehler ausgegeben. Es soll einen Importfehler auslösen, wenn kein Modul vorhanden ist. Auf diese Weise können Sie es fangen, wenn Sie müssen. Das Problem mit den anderen Lösungen ist, dass sie andere Fehler maskieren.
Enpenax
Versuchen / außer bedeutet nicht, dass Sie nicht protokollieren oder sicherstellen dürfen. Sie können jeden zugrunde liegenden Traceback vollständig abfangen und alles tun, was Sie wollen.
Zulu
8

go_as 'Antwort als Einzeiler

 python -c "help('modules');" | grep module

quelle
6

Ich bin auf diese Frage gestoßen, als ich nach einer Möglichkeit gesucht habe, zu überprüfen, ob ein Modul über die Befehlszeile geladen wurde und möchte meine Gedanken für diejenigen teilen, die nach mir kommen und nach demselben suchen:

Linux / UNIX-Skriptdateimethode : Erstellen Sie eine Datei module_help.py:

#!/usr/bin/env python

help('modules')

Stellen Sie dann sicher, dass es ausführbar ist: chmod u+x module_help.py

Und nenne es mit einem pipezu grep:

./module_help.py | grep module_name

Rufen Sie das integrierte Hilfesystem auf . (Diese Funktion ist für die interaktive Verwendung vorgesehen .) Wenn kein Argument angegeben wird, wird das interaktive Hilfesystem auf der Interpreter-Konsole gestartet. Wenn das Argument eine Zeichenfolge ist , wird die Zeichenfolge als Name eines Moduls , einer Funktion, einer Klasse, einer Methode, eines Schlüsselworts oder eines Dokumentationsthemas nachgeschlagen und eine Hilfeseite auf der Konsole gedruckt. Wenn es sich bei dem Argument um eine andere Art von Objekt handelt, wird eine Hilfeseite für das Objekt generiert.

Interaktive Methode : in der Konsole ladenpython

>>> help('module_name')

Wenn gefunden, beenden Sie das Lesen durch Eingabe von. q
Um die interaktive Python-Sitzung zu beenden, drücken Sie Ctrl+D

Windows-Skriptdateimethode auch Linux / UNIX-kompatibel und insgesamt besser :

#!/usr/bin/env python

import sys

help(sys.argv[1])

Rufen Sie es vom Befehl aus wie folgt auf:

python module_help.py site  

Würde ausgeben:

Hilfe auf der Modulseite:

NAME site - Hängt Modulsuchpfade für Pakete von Drittanbietern an sys.path an.

FILE /usr/lib/python2.7/site.py

MODULE DOCS http://docs.python.org/library/site

DESCRIPTION
...
:

und Sie müssten drücken q, um den interaktiven Modus zu verlassen.

Verwenden Sie es unbekanntes Modul:

python module_help.py lkajshdflkahsodf

Würde ausgeben:

Für 'lkajshdflkahsodf' wurde keine Python-Dokumentation gefunden.

und verlassen.

go_as
quelle
5

Verwenden Sie eine der Funktionen von pkgutil , zum Beispiel:

from pkgutil import iter_modules

def module_exists(module_name):
    return module_name in (name for loader, name, ispkg in iter_modules())
ArtOfWarfare
quelle
4

Sie könnten einfach ein kleines Skript schreiben, das versucht, alle Module zu importieren und Ihnen mitzuteilen, welche fehlerhaft sind und welche funktionieren:

import pip


if __name__ == '__main__':
    for package in pip.get_installed_distributions():
        pack_string = str(package).split(" ")[0]
        try:
            if __import__(pack_string.lower()):
                print(pack_string + " loaded successfully")
        except Exception as e:
            print(pack_string + " failed with error code: {}".format(e))

Ausgabe:

zope.interface loaded successfully
zope.deprecation loaded successfully
yarg loaded successfully
xlrd loaded successfully
WMI loaded successfully
Werkzeug loaded successfully
WebOb loaded successfully
virtualenv loaded successfully
...

Ein Wort der Warnung, dies wird versuchen, alles zu importieren , so dass Sie Dinge wie sehen werden, PyYAML failed with error code: No module named pyyamlweil der tatsächliche Importname nur yaml ist. Solange Sie Ihre Importe kennen, sollte dies den Trick für Sie tun.

User9123
quelle
3

Ich habe diese Hilfsfunktion geschrieben:

def is_module_available(module_name):
    if sys.version_info < (3, 0):
        # python 2
        import importlib
        torch_loader = importlib.find_loader(module_name)
    elif sys.version_info <= (3, 3):
        # python 3.0 to 3.3
        import pkgutil
        torch_loader = pkgutil.find_loader(module_name)
    elif sys.version_info >= (3, 4):
        # python 3.4 and above
        import importlib
        torch_loader = importlib.util.find_spec(module_name)

    return torch_loader is not None
Fardin
quelle
2

Sie können auch importlibdirekt verwenden

import importlib

try:
    importlib.import_module(module_name)
except ImportError:
    # Handle error
Jackotonye
quelle
Dies hat das Problem, es tatsächlich zu importieren. Nebenwirkungen und alles
Yarbelk
2

Es gibt keine Möglichkeit, zuverlässig zu überprüfen, ob "gepunktetes Modul" importierbar ist, ohne das übergeordnete Paket zu importieren. Vor diesem Hintergrund gibt es viele Lösungen für das Problem "Überprüfen, ob ein Python-Modul vorhanden ist".

Die folgende Lösung behebt das Problem, dass das importierte Modul ImportError auslösen kann, selbst wenn es existiert. Wir wollen diese Situation von einer Situation unterscheiden, in der kein Modul existiert.

Python 2 :

import importlib
import pkgutil
import sys

def find_module(full_module_name):
    """
    Returns module object if module `full_module_name` can be imported. 

    Returns None if module does not exist. 

    Exception is raised if (existing) module raises exception during its import.
    """
    module = sys.modules.get(full_module_name)
    if module is None:
        module_path_tail = full_module_name.split('.')
        module_path_head = []
        loader = True
        while module_path_tail and loader:
            module_path_head.append(module_path_tail.pop(0))
            module_name = ".".join(module_path_head)
            loader = bool(pkgutil.find_loader(module_name))
            if not loader:
                # Double check if module realy does not exist
                # (case: full_module_name == 'paste.deploy')
                try:
                    importlib.import_module(module_name)
                except ImportError:
                    pass
                else:
                    loader = True
        if loader:
            module = importlib.import_module(full_module_name)
    return module

Python 3 :

import importlib

def find_module(full_module_name):
    """
    Returns module object if module `full_module_name` can be imported. 

    Returns None if module does not exist. 

    Exception is raised if (existing) module raises exception during its import.
    """
    try:
        return importlib.import_module(full_module_name)
    except ImportError as exc:
        if not (full_module_name + '.').startswith(exc.name + '.'):
            raise
Marcin Raczyński
quelle
1

in django.utils.module_loading.module_has_submodule


import sys
import os
import imp

def module_has_submodule(package, module_name):
    """
    check module in package
    django.utils.module_loading.module_has_submodule
    """
    name = ".".join([package.__name__, module_name])
    try:
        # None indicates a cached miss; see mark_miss() in Python/import.c.
        return sys.modules[name] is not None
    except KeyError:
        pass
    try:
        package_path = package.__path__   # No __path__, then not a package.
    except AttributeError:
        # Since the remainder of this function assumes that we're dealing with
        # a package (module with a __path__), so if it's not, then bail here.
        return False
    for finder in sys.meta_path:
        if finder.find_module(name, package_path):
            return True
    for entry in package_path:
        try:
            # Try the cached finder.
            finder = sys.path_importer_cache[entry]
            if finder is None:
                # Implicit import machinery should be used.
                try:
                    file_, _, _ = imp.find_module(module_name, [entry])
                    if file_:
                        file_.close()
                    return True
                except ImportError:
                    continue
            # Else see if the finder knows of a loader.
            elif finder.find_module(name):
                return True
            else:
                continue
        except KeyError:
            # No cached finder, so try and make one.
            for hook in sys.path_hooks:
                try:
                    finder = hook(entry)
                    # XXX Could cache in sys.path_importer_cache
                    if finder.find_module(name):
                        return True
                    else:
                        # Once a finder is found, stop the search.
                        break
                except ImportError:
                    # Continue the search for a finder.
                    continue
            else:
                # No finder found.
                # Try the implicit import machinery if searching a directory.
                if os.path.isdir(entry):
                    try:
                        file_, _, _ = imp.find_module(module_name, [entry])
                        if file_:
                            file_.close()
                        return True
                    except ImportError:
                        pass
                # XXX Could insert None or NullImporter
    else:
        # Exhausted the search, so the module cannot be found.
        return False
dibrovsd
quelle
Dies erfüllt meine Standardfrage beim Programmieren von Python: WWDD (Was würde Django tun?) Und ich hätte dort nachsehen sollen
yarbelk