Comment vérifier si un module python existe sans l'importer

179

J'ai besoin de savoir si un module python existe, sans l'importer.

Importer quelque chose qui pourrait ne pas exister (pas ce que je veux):

try:
    import eggs
except ImportError:
    pass
yarbelk
la source
4
Je suis curieux de savoir quels sont les inconvénients de l'utilisation de l'importation?
Chuck
15
Si votre module a des effets secondaires, l'appel d'importation peut avoir des conséquences indésirables. Donc, si vous souhaitez vérifier quelle version d'un fichier exécuter en premier, vous pouvez vérifier avec la réponse ci-dessous et effectuer l'importation plus tard. Je ne dis pas que c'est une bonne idée d'écrire des modules avec des effets secondaires - mais nous sommes tous des adultes et pouvons prendre nos propres décisions quant à la dangerosité avec laquelle nous voulons coder.
yarbelk
1
@ArtOfWarfare Je viens de fermer cette question que vous avez liée en double de celle- ci. Parce que cette question est plus claire et que la solution proposée ici est meilleure que toutes les autres énumérées ici. Je préfère pointer celui qui veut une réponse à cette meilleure solution plutôt que d'en éloigner les gens.
Bakuriu
7
@Chuck De plus, le module peut exister, mais peut lui-même contenir des erreurs d'importation. La capture d'ImportErrors comme dans le code ci-dessus pourrait conduire à indiquer que le module n'existe pas, alors qu'il existe effectivement des erreurs.
Michael Barton

Réponses:

199

Python2

Pour vérifier si l'importation peut trouver quelque chose dans python2, utilisez imp

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

Pour trouver des importations pointées, vous devez faire plus:

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

Vous pouvez également utiliser pkgutil.find_loader(plus ou moins le même que la partie python3

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

Python3

Python3 ≤ 3,3

Vous devriez utiliser importlib, Comment j'ai fait cela était:

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

Mon attente étant, si vous pouvez trouver un chargeur pour cela, alors il existe. Vous pouvez également être un peu plus intelligent à ce sujet, comme filtrer les chargeurs que vous accepterez. Par exemple:

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

Dans Python3.4, la importlib.find_loader documentation python était obsolète au profit de importlib.util.find_spec. La méthode recommandée est la importlib.util.find_spec. Il y en a d'autres comme importlib.machinery.FileFinder, ce qui est utile si vous recherchez un fichier spécifique à charger. Comprendre comment les utiliser dépasse le cadre de cela.

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

Cela fonctionne également avec les importations relatives, mais vous devez fournir le package de départ, vous pouvez donc également faire:

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

Bien que je sois sûr qu'il existe une raison de faire cela, je ne suis pas sûr de ce que ce serait.

AVERTISSEMENT

Lorsque vous essayez de trouver un sous-module, il importera le module parent (pour toutes les méthodes ci-dessus)!

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

commentaires bienvenus pour contourner ce problème

Remerciements

  • @rvighne pour importlib
  • @ lucas-guido pour python3.3 + dépréciation find_loader
  • @enpenax pour le comportement de pkgutils.find_loader en python2.7
yarbelk
la source
3
Cela ne fonctionne que pour les modules de niveau supérieur, pas pour eggs.ham.spam.
hemflit
3
@hemflit si vous voulez trouver spamen eggs.hamvous utiliseriezimp.find_module('spam', ['eggs', 'ham'])
gitaarik
5
+1, mais impest obsolète au profit de importlibPython 3.
rvighne
4
Que faire si le module importé contient une "ImportError" réelle. C'est ce qui m'arrivait. Alors le module existe mais ne sera pas "trouvé".
enpenax
1
Après un an, je suis tombé sur le même problème que j'ai mentionné juste ci-dessus et je cherchais une solution pour Python 2: pkgutil.find_loader("my.package.module")retourne un chargeur si le package / module existe et Nonesinon. Veuillez mettre à jour votre réponse pour Python 2, car le masquage de l'ImportError m'a rendu fou hier xP
enpenax
13

Après avoir utilisé la réponse de yarbelk, je l'ai fait pour ne pas avoir à importer ìmp.

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

Utile dans Django settings.pypar exemple.

zoulou
la source
4
J'ai voté contre, car cela masque les erreurs d'importation dans le module, ce qui rend très difficile la détection de l'erreur.
enpenax
1
Le vote négatif est une mauvaise idée, une bonne pratique est de "toujours enregistrer les erreurs détectées". Ceci est un exemple après avoir écrit comment vous le souhaitez.
Zulu
2
Comment consigneriez-vous une erreur si le module importé échoue sur la ligne 1 avec un ImportError et que votre try catch le fait échouer en silence?
enpenax
Je viens de rencontrer le problème des erreurs de masquage d'importation dans la vraie vie, et c'était mauvais (provoquant des tests qui auraient dû échouer!).
David donné le
Je suis tombé sur celui où quelqu'un utilisait cette erreur pour déclencher un monkeypatch sur un module différent ... c'était de la folie à trouver
yarbelk
13

Python 3> = 3.6: ModuleNotFoundError

Le ModuleNotFoundErrora été introduit dans python 3.6 et peut être utilisé à cette fin

try:
    import eggs
except ModuleNotFoundError:
    # Error handling
    pass

L'erreur est générée lorsqu'un module ou l'un de ses parents est introuvable. Alors

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

affichera un message qui ressemble à No module named 'eggs'si le eggsmodule est introuvable; mais afficherait quelque chose comme No module named 'eggs.sub'si seul le submodule ne pouvait pas être trouvé mais que le eggspaquet pouvait être trouvé.

Consultez la documentation du système d'importation pour plus d'informations sur leModuleNotFoundError

J.Baoby
la source
1
Cela ne répond pas à la question car il importe le package s'il existe
divenex
11

Python 2, sans compter ImportError

Jusqu'à ce que la réponse actuelle soit mise à jour, voici le chemin pour Python 2

import pkgutil
import importlib

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

Pourquoi une autre réponse?

Beaucoup de réponses utilisent la capture d'un fichier ImportError. Le problème avec cela est que nous ne pouvons pas savoir ce qui jette le ImportError.

Si vous importez votre module existant et qu'il se trouve qu'il y en a un ImportErrordans votre module (par exemple une faute de frappe sur la ligne 1), le résultat sera que votre module n'existe pas. Il vous faudra beaucoup de retour en arrière pour comprendre que votre module existe et qu'il ImportErrorest intercepté et fait échouer les choses en silence.

enpenax
la source
Cela n'a peut-être pas été clair, mais tous les blocs de code, sauf les premiers, ne reposent pas sur ImportError- veuillez modifier si cela ne vous était pas clair.
yarbelk
Je vous vois utiliser la capture ImportError dans les deux premiers exemples de Python 2. Pourquoi sont-ils là, alors?
enpenax
3
Cela lève ImportError si mod == 'not_existing_package.mymodule'. Voir ma solution
Marcin Raczyński
1
Bien sûr, cela génère une erreur d'importation. Il est censé générer une erreur d'importation si un module n'existe pas. De cette façon, vous pouvez l'attraper si vous en avez besoin. Le problème avec les autres solutions est qu'elles masquent d'autres erreurs.
enpenax
Essayer / sauf ne signifie pas que vous ne devez pas vous connecter ou vous assurer des choses. Vous pouvez capturer complètement tout traceback sous-jacent et faire tout ce que vous voulez.
Zulu
8

La réponse de go_as en une seule ligne

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

la source
6

Je suis tombé sur cette question en cherchant un moyen de vérifier si un module est chargé à partir de la ligne de commande et j'aimerais partager mes réflexions sur ceux qui viennent après moi et à la recherche de la même chose:

Méthode de fichier de script Linux / UNIX : créer un fichier module_help.py:

#!/usr/bin/env python

help('modules')

Ensuite, assurez-vous qu'il est exécutable: chmod u+x module_help.py

Et appelez-le avec un pipeà grep:

./module_help.py | grep module_name

Appelez le système d'aide intégré . (Cette fonction est destinée à une utilisation interactive .) Si aucun argument n'est donné, le système d'aide interactif démarre sur la console d'interprétation. Si l'argument est une chaîne , la chaîne est recherchée comme le nom d'un module , d'une fonction, d'une classe, d'une méthode, d'un mot-clé ou d'une rubrique de documentation, et une page d'aide est imprimée sur la console. Si l'argument est un autre type d'objet, une page d'aide sur l'objet est générée.

Méthode interactive : dans le chargement de la consolepython

>>> help('module_name')

Si trouvé, quittez la lecture en tapant q
Pour quitter la session interactive python, appuyez sur Ctrl+D

Méthode de fichier de script Windows également compatible Linux / UNIX, et meilleure dans l'ensemble :

#!/usr/bin/env python

import sys

help(sys.argv[1])

L'appel à partir de la commande comme:

python module_help.py site  

Produirait:

Aide sur le site du module:

NAME site - Ajoutez les chemins de recherche de module pour les packages tiers à sys.path.

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

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

DESCRIPTION
...
:

et vous devrez appuyer sur qpour quitter le mode interactif.

En utilisant ce module inconnu:

python module_help.py lkajshdflkahsodf

Produirait:

aucune documentation Python trouvée pour 'lkajshdflkahsodf'

et sortir.

go_as
la source
5

Utilisez l'une des fonctions de pkgutil , par exemple:

from pkgutil import iter_modules

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

Vous pouvez simplement écrire un petit script qui essaierait d'importer tous les modules et vous dirait lesquels échouent et lesquels fonctionnent:

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

Production:

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

Parole d'avertissement, cela essaiera de tout importer pour que vous voyiez des choses comme PyYAML failed with error code: No module named pyyamlparce que le nom d'importation réel est juste yaml. Donc, tant que vous connaissez vos importations, cela devrait faire l'affaire pour vous.

Utilisateur9123
la source
3

J'ai écrit cette fonction d'assistance:

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
la source
2

Vous pouvez également utiliser importlibdirectement

import importlib

try:
    importlib.import_module(module_name)
except ImportError:
    # Handle error
jackotonye
la source
Cela pose le problème de l'importer. Effets secondaires et tout
yarbelk
2

Il n'y a aucun moyen de vérifier de manière fiable si le "module pointillé" est importable sans importer son package parent. En disant cela, il existe de nombreuses solutions au problème "comment vérifier si le module Python existe".

La solution ci-dessous résout le problème que le module importé peut soulever ImportError même s'il existe. Nous voulons distinguer cette situation de celle dans laquelle le module n'existe pas.

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
la source
1

dans 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
la source
Cela répond à ma question standard lors de la programmation de python: WWDD (Que ferait Django?) Et j'aurais dû y regarder
yarbelk