Come verificare se esiste un modulo Python senza importarlo


179

Devo sapere se esiste un modulo Python, senza importarlo.

Importare qualcosa che potrebbe non esistere (non quello che voglio):

try:
    import eggs
except ImportError:
    pass

4
Sono curioso, quali sono gli svantaggi dell'utilizzo dell'importazione?
Chuck,

15
Se il tuo modulo ha effetti collaterali, chiamare l'importazione potrebbe avere conseguenze indesiderate. Quindi, se si desidera verificare quale versione di un file deve essere eseguita per prima, è possibile verificare con la risposta di seguito ed eseguire l'importazione in un secondo momento. Non sto suggerendo che sia una buona idea scrivere moduli con effetti collaterali - ma siamo tutti adulti e possiamo prendere le nostre decisioni su quanto pericolosamente vogliamo codificare.
Yarbelk,


1
@ArtOfWarfare Ho appena chiuso la domanda che hai collegato come duplicato di questo . Perché questa domanda è più chiara e anche la soluzione proposta qui è migliore di tutte le altre elencate lì. Preferirei indicare a chiunque voglia una risposta a questa soluzione migliore che allontanare le persone da essa.
Bakuriu,

7
@Chuck Inoltre il modulo potrebbe esistere, ma potrebbe contenere errori di importazione. La cattura di ImportErrors come nel codice sopra potrebbe portare a indicare che il modulo non esiste, quando in realtà lo fa ma presenta errori.
Michael Barton,

Risposte:


199

python2

Per verificare se l'importazione può trovare qualcosa in python2, usando imp

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

Per trovare le importazioni punteggiate, devi fare di più:

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

Puoi anche usare pkgutil.find_loader(più o meno lo stesso della parte python3

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

python3

Python3 ≤ 3.3

Dovresti usare importlib, Come ho fatto per fare questo è stato:

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

La mia aspettativa è, se riesci a trovare un caricatore per esso, allora esiste. Puoi anche essere un po 'più intelligente a riguardo, come filtrare quali caricatori accetterai. Per esempio:

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 i importlib.find_loader documenti di Python sono stati deprecati a favore di importlib.util.find_spec. Il metodo raccomandato è il importlib.util.find_spec. Ce ne sono altri simili importlib.machinery.FileFinder, utili se si desidera caricare un file specifico. Capire come usarli è oltre lo scopo di questo.

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

Questo funziona anche con le importazioni relative ma è necessario fornire il pacchetto iniziale, quindi è possibile anche:

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

Anche se sono sicuro che esiste una ragione per farlo - non sono sicuro di cosa sarebbe.

AVVERTIMENTO

Quando si cerca di trovare un sottomodulo, importerà il modulo genitore (per tutti i metodi sopra)!

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')

commenti benvenuti su come aggirare questo

Ringraziamenti

  • @rvighne per importlib
  • @ lucas-guido per python3.3 + depricante find_loader
  • @enpenax per il comportamento di pkgutils.find_loader in python2.7

3
Questo funziona solo per i moduli di livello superiore, non per eggs.ham.spam.
hemflit

3
@hemflit se si vuole trovare spamin eggs.hamuseresteimp.find_module('spam', ['eggs', 'ham'])
gitaarik

5
+1, ma impè deprecato in favore di importlibPython 3.
rvighne,

4
Che cosa succede se il modulo importato contiene un vero "ImportError". Questo è quello che mi stava succedendo. Quindi il modulo esiste ma non verrà "trovato".
enpenax,

1
Dopo un anno mi sono imbattuto nello stesso problema che ho menzionato sopra e stavo cercando una soluzione per Python 2: pkgutil.find_loader("my.package.module")restituisce un caricatore se il pacchetto / modulo esiste e Nonese no. Aggiorna la tua risposta per Python 2, poiché mascherare ImportError mi ha fatto impazzire ieri xP
enpenax,

13

Dopo aver usato la risposta di Yarbelk, l'ho fatto per non dover importare ìmp.

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

Utile in Django settings.pyper esempio.


4
Ho effettuato il downgrade, perché questo maschera gli errori di importazione nel modulo, il che rende davvero difficile individuare l'errore.
enpenax,

1
Il downvote è una cattiva idea, una buona pratica è "registrare sempre errori rilevati". Questo è un esempio dopo aver scritto come lo desideri.
Zulu,

2
Come registreresti un errore se il modulo importato fallisce sulla linea 1 con un ImportError e il tuo tentativo di cattura lo fa fallire silenziosamente?
enpenax,

Ho appena incontrato il problema degli errori di importazione-mascheramento nella vita reale, ed è stato male (causando test che avrebbero dovuto fallire!).
David, dato il

Mi sono imbattuto in quello in cui qualcuno stava usando
quell'errore

13

Python 3> = 3.6: ModuleNotFoundError

Il softwareModuleNotFoundError è stato introdotto in Python 3.6 e può essere utilizzato per questo scopo

try:
    import eggs
except ModuleNotFoundError:
    # Error handling
    pass

L'errore viene generato quando non è possibile trovare un modulo o uno dei suoi genitori . Così

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

stamperebbe un messaggio che assomiglia No module named 'eggs'se eggsnon è possibile trovare il modulo; ma stamperebbe qualcosa come No module named 'eggs.sub'se subnon fosse possibile trovare solo il modulo ma il eggspacchetto potrebbe essere trovato.

Consultare la documentazione del sistema di importazione per ulteriori informazioni suModuleNotFoundError


1
Questo non risponde alla domanda perché importa il pacchetto se esiste
divenex

11

Python 2, senza fare affidamento su ImportError

Fino a quando la risposta corrente non viene aggiornata, ecco la strada per Python 2

import pkgutil
import importlib

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

Perché un'altra risposta?

Molte risposte sfruttano la cattura di un ImportError. Il problema è che non possiamo sapere cosa genera ImportError.

Se si importa il modulo esistente e nel modulo si verifica un ImportErrorerrore (ad es. Errore di battitura nella riga 1), il risultato sarà che il modulo non esiste. Ci vorrà una grande quantità di backtracking per capire che il modulo esiste e che ImportErrorviene catturato e fa sì che le cose falliscano silenziosamente.


Potrebbe non essere chiaro, ma tutti tranne i primi blocchi di codice non si basano su ImportError: modifica se non ti è chiaro.
Yarbelk,

Vedo che usi il fermo ImportError nei primi due esempi di Python 2. Perché sono lì allora?
enpenax,

3
Questo genera ImportError se mod == 'not_existing_package.mymodule'. Vedi la mia soluzione
Marcin Raczyński,

1
Ovviamente genera un errore di importazione. Dovrebbe generare un errore di importazione se non esiste un modulo. In questo modo puoi prenderlo se necessario. Il problema con le altre soluzioni è che mascherano altri errori.
enpenax,

Provare / tranne non significa che non è necessario accedere o garantire le cose. Puoi catturare completamente qualsiasi traceback sottostante e fare tutto quello che vuoi.
Zulu

8

La risposta di go_as come una fodera

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

6

Mi sono imbattuto in questa domanda durante la ricerca di un modo per verificare se un modulo è caricato dalla riga di comando e vorrei condividere i miei pensieri per quelli che mi seguono e cercano lo stesso:

Metodo del file di script Linux / UNIX : crea un file module_help.py:

#!/usr/bin/env python

help('modules')

Quindi assicurati che sia eseguibile: chmod u+x module_help.py

E chiamalo con un pipea grep:

./module_help.py | grep module_name

Richiama il built-in sistema di aiuto integrato . (Questa funzione è destinata all'uso interattivo .) Se non viene fornito alcun argomento, il sistema di aiuto interattivo si avvia sulla console dell'interprete. Se l'argomento è una stringa , la stringa viene cercata come nome di un argomento relativo a modulo , funzione, classe, metodo, parola chiave o documentazione e sulla console viene stampata una pagina di aiuto. Se l'argomento è qualsiasi altro tipo di oggetto, viene generata una pagina di aiuto sull'oggetto.

Metodo interattivo : nel caricamento della consolepython

>>> help('module_name')

Se si trova, chiudere la lettura digitando q
Per uscire dalla sessione interattiva di Python, premere Ctrl+D

Metodo del file di script di Windows compatibile anche con Linux / UNIX e , in generale , migliore :

#!/usr/bin/env python

import sys

help(sys.argv[1])

Chiamandolo dal comando come:

python module_help.py site  

Uscita:

Aiuto sul sito del modulo:

NAME site - Aggiunge percorsi di ricerca del modulo per pacchetti di terze parti a sys.path.

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

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

DESCRIPTION
...
:

e dovresti premere q per uscire dalla modalità interattiva.

Usandolo modulo sconosciuto:

python module_help.py lkajshdflkahsodf

Uscita:

nessuna documentazione Python trovata per 'lkajshdflkahsodf'

ed esci.


5

Utilizzare una delle funzioni di pkgutil , ad esempio:

from pkgutil import iter_modules

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


4

Potresti semplicemente scrivere un piccolo script che provi a importare tutti i moduli e ti dica quali non funzionano e quali funzionano:

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))

Produzione:

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

Un avvertimento questo tenterà di importare tutto in modo da vedere cose come PyYAML failed with error code: No module named pyyamlperché il nome di importazione effettivo è solo yaml. Quindi, se conosci le tue importazioni, questo dovrebbe fare al caso tuo.


3

Ho scritto questa funzione di supporto:

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

2

Puoi anche usare importlibdirettamente

import importlib

try:
    importlib.import_module(module_name)
except ImportError:
    # Handle error

Questo ha il problema di importarlo effettivamente. Effetti collaterali e tutti
yarbelk

2

Non è possibile verificare in modo affidabile se il "modulo punteggiato" è importabile senza importare il pacchetto padre. Detto questo, ci sono molte soluzioni al problema "come verificare se esiste il modulo Python".

La soluzione seguente risolve il problema che il modulo importato può generare ImportError anche se esiste. Vogliamo distinguere quella situazione da tale in cui il modulo non esiste.

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

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

Questo soddisfa la mia domanda standard durante la programmazione di Python: WWDD (cosa farebbe Django?) E dovrei guardare lì
Yarbelk,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.