Cómo verificar si existe un módulo de Python sin importarlo


179

Necesito saber si existe un módulo de Python, sin importarlo.

Importar algo que podría no existir (no es lo que quiero):

try:
    import eggs
except ImportError:
    pass

44
Tengo curiosidad, ¿cuáles son las desventajas de usar la importación?
Chuck

15
Si su módulo tiene efectos secundarios, llamar a importar podría tener consecuencias no deseadas. Entonces, si desea verificar qué versión de un archivo se ejecuta primero, puede verificar con la respuesta a continuación y realizar la importación más tarde. No estoy sugiriendo que sea una buena idea escribir módulos con efectos secundarios, pero todos somos adultos y podemos tomar nuestras propias decisiones sobre cuán peligrosamente queremos codificar.
yarbelk


1
@ArtOfWarfare Acabo de cerrar esa pregunta que vinculaste como un duplicado de esta . Debido a que esta pregunta es más clara y también la solución propuesta aquí es mejor que todas las demás enumeradas allí. Prefiero señalar a quien quiera una respuesta a esta mejor solución que alejar a la gente de ella.
Bakuriu

77
@Chuck Además, el módulo puede existir, pero puede contener errores de importación. La captura de errores de importación como en el código anterior podría llevar a indicar que el módulo no existe, si es que sí, pero tiene errores.
Michael Barton

Respuestas:


199

Python2

Para verificar si la importación puede encontrar algo en python2, usando imp

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

Para encontrar importaciones punteadas, debe hacer más:

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

También puede usar pkgutil.find_loader(más o menos lo mismo que la parte python3

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

Python3

Python3 ≤ 3.3

Deberías usar importlib, Cómo hice esto fue:

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

Mi expectativa es que si puedes encontrar un cargador para él, entonces existe. También puede ser un poco más inteligente al respecto, como filtrar los cargadores que aceptará. Por ejemplo:

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

En Python3.4, los importlib.find_loader documentos de Python quedaron en desuso a favor de importlib.util.find_spec. El método recomendado es el importlib.util.find_spec. Hay otros como importlib.machinery.FileFinder, lo que es útil si buscas un archivo específico para cargar. Descubrir cómo usarlos está más allá del alcance de esto.

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

Esto también funciona con importaciones relativas, pero debe proporcionar el paquete inicial, por lo que también podría hacer:

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

Si bien estoy seguro de que existe una razón para hacerlo, no estoy seguro de cuál sería.

ADVERTENCIA

¡Al intentar encontrar un submódulo, importará el módulo principal (para todos los métodos anteriores)!

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

comentarios bienvenidos para evitar esto

Agradecimientos

  • @rvighne para importlib
  • @ lucas-guido para python3.3 + depricating find_loader
  • @enpenax para el comportamiento de pkgutils.find_loader en python2.7

3
Esto solo funciona para módulos de nivel superior, no para eggs.ham.spam.
Hemflit

3
@hemflit si quieres encontrar spamen eggs.hamti imp.find_module('spam', ['eggs', 'ham'])
usarías

55
+1, pero impestá en desuso a favor de importlibPython 3.
rvighne

44
¿Qué pasa si el módulo importado contiene un "ImportError" real? Eso es lo que me estaba pasando. Entonces el módulo existe pero no será "encontrado".
enpenax

1
Después de un año, me encontré con el mismo problema que mencioné anteriormente y estaba buscando una solución para Python 2: pkgutil.find_loader("my.package.module")devuelve un cargador si el paquete / módulo existe y Nonesi no. Actualice su respuesta para Python 2, ya que enmascarar el ImportError me volvió loco ayer xP
enpenax

13

Después de usar la respuesta de yarbelk, hice esto para no tener que importar ìmp.

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

Útil en Django settings.pypor ejemplo.


44
Voté en contra, porque esto oculta errores de importación en el módulo, lo que hace que sea muy difícil detectar el error.
enpenax

1
Votar a favor es una mala idea, una buena práctica es "siempre registrar errores capturados". Este es un ejemplo después de escribir cómo lo quiere.
Zulu

2
¿Cómo registraría un error si el módulo importado falla en la línea 1 con un ImportError y su intento de captura hace que falle silenciosamente?
enpenax

Acabo de encontrarme con el problema de enmascarar-importar-errores en la vida real, y fue malo (¡causando pruebas que deberían haber fallado!).
David dado el

Me encontré con que, cuando alguien estaba usando ese error para desencadenar una monkeypatch en un módulo diferente ... que era una locura para encontrar
yarbelk

13

Python 3> = 3.6: ModuleNotFoundError

Se ModuleNotFoundErrorha introducido en Python 3.6 y se puede utilizar para este propósito.

try:
    import eggs
except ModuleNotFoundError:
    # Error handling
    pass

El error se genera cuando no se puede encontrar un módulo o uno de sus padres . Entonces

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

imprimiría un mensaje que se vería No module named 'eggs'si el eggsmódulo no se puede encontrar; pero imprimiría algo como No module named 'eggs.sub'si subno se pudiera encontrar el módulo pero se pudiera encontrar el eggspaquete.

Consulte la documentación del sistema de importación para obtener más información sobreModuleNotFoundError


1
Esto no responde la pregunta porque importa el paquete si existe
divenex

11

Python 2, sin depender de ImportError

Hasta que se actualice la respuesta actual, este es el camino para Python 2

import pkgutil
import importlib

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

¿Por qué otra respuesta?

Muchas respuestas hacen uso de la captura de un ImportError. El problema con eso es que no podemos saber qué arroja el ImportError.

Si importa su loro módulo y sucede que hay una ImportErroren el módulo (por ejemplo, error tipográfico en la línea 1), el resultado será que el módulo no existe. Le llevará bastante retroceso descubrir que su módulo existe y que ImportErrorestá atrapado y hace que las cosas fallen silenciosamente.


Es posible que no haya quedado claro, pero no todos los primeros bloques de código dependen ImportError; edite si no le quedó claro.
yarbelk

Te veo usando la captura ImportError en los primeros dos ejemplos de Python 2. ¿Por qué están allí, entonces?
enpenax

3
Esto arroja ImportError si mod == 'not_existing_package.mymodule'. Vea mi solución
Marcin Raczyński el

1
Por supuesto, arroja un error de importación. Se supone que arroja un error de importación si no existe un módulo. De esa manera puedes atraparlo si lo necesitas. El problema con las otras soluciones es que ocultan otros errores.
enpenax

Probar / excepto no significa que no debe iniciar sesión ni garantizar las cosas. Puede capturar completamente cualquier rastreo subyacente y hacer lo que quiera con.
Zulu

8

La respuesta de go_as como un trazador de líneas

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

6

Me encontré con esta pregunta mientras buscaba una forma de verificar si un módulo está cargado desde la línea de comandos y me gustaría compartir mis pensamientos sobre los que vienen después de mí y buscan lo mismo:

Método de archivo de script Linux / UNIX : cree un archivo module_help.py:

#!/usr/bin/env python

help('modules')

Luego asegúrese de que sea ejecutable: chmod u+x module_help.py

Y llámalo con un pipepara grep:

./module_help.py | grep module_name

Invoque el sistema de ayuda incorporado . (Esta función está destinada para uso interactivo .) Si no se proporciona ningún argumento, el sistema de ayuda interactivo se inicia en la consola del intérprete. Si el argumento es una cadena , se busca la cadena como el nombre de un módulo , función, clase, método, palabra clave o tema de documentación, y se imprime una página de ayuda en la consola. Si el argumento es cualquier otro tipo de objeto, se genera una página de ayuda sobre el objeto.

Método interactivo : en la carga de la consolapython

>>> help('module_name')

Si se encuentra, deje de leer escribiendo q
Para salir de la sesión interactiva de Python, presione Ctrl+D

El método de archivo de script de Windows también es compatible con Linux / UNIX, y mejor en general :

#!/usr/bin/env python

import sys

help(sys.argv[1])

Llamándolo desde el comando como:

python module_help.py site  

Daría salida:

Ayuda en el sitio del módulo:

NAME sitio: agregue rutas de búsqueda de módulos para paquetes de terceros a sys.path.

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

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

DESCRIPTION
...
:

y tendrías que presionar qpara salir del modo interactivo.

Utilizándolo módulo desconocido:

python module_help.py lkajshdflkahsodf

Daría salida:

no se encontró documentación de Python para 'lkajshdflkahsodf'

y salir


5

Utilice una de las funciones de pkgutil , por ejemplo:

from pkgutil import iter_modules

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


4

Puede escribir un pequeño script que intente importar todos los módulos y decirle cuáles fallan y cuáles funcionan:

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

Salida:

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

Una advertencia: esto intentará importar todo para que veas cosas como PyYAML failed with error code: No module named pyyamlporque el nombre de importación real es solo yaml. Entonces, siempre que conozca sus importaciones, esto debería ser el truco para usted.


3

Escribí esta función auxiliar:

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

También puedes usar importlibdirectamente

import importlib

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

Esto tiene el problema de importarlo realmente. Efectos secundarios y todo
yarbelk

2

No hay forma de verificar de manera confiable si el "módulo de puntos" es importable sin importar su paquete principal. Dicho esto, hay muchas soluciones al problema "cómo verificar si existe el módulo Python".

La solución a continuación soluciona el problema de que el módulo importado puede generar ImportError incluso si existe. Queremos distinguir esa situación de aquella en la que el módulo no existe.

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

en 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

Con ello se cumple mi pregunta estándar cuando programación Python: WWDD (? ¿Cómo sería Django Do) y yo debería tener aspecto no
yarbelk
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.