Como verificar se existe um módulo python sem importá-lo


179

Eu preciso saber se existe um módulo python, sem importá-lo.

Importando algo que pode não existir (não o que eu quero):

try:
    import eggs
except ImportError:
    pass

4
Estou curioso, quais são as desvantagens de usar a importação?
— Chuck

15
Se o seu módulo tiver efeitos colaterais, chamar a importação pode ter consequências indesejadas. Portanto, se você quiser verificar qual versão de um arquivo será executada primeiro, verifique com a resposta abaixo e faça a importação mais tarde. Não estou sugerindo que seja uma boa ideia escrever módulos com efeitos colaterais - mas somos todos adultos e podemos tomar nossas próprias decisões sobre o quão perigosamente queremos codificar.
— precisa saber é


1
@ArtOfWarfare Acabei de encerrar a pergunta que você vinculou como duplicata desta . Como essa pergunta é mais clara e também a solução proposta aqui é melhor do que todas as outras listadas lá. Prefiro apontar quem quer uma resposta para essa solução melhor do que afastar as pessoas dela.
— Bakuriu

7
@Chuck Além disso, o módulo pode existir, mas pode conter erros de importação. A captura de ImportErrors como no código acima pode levar à indicação de que o módulo não existe, se houver, mas houver erros.
— Michael Barton

Respostas:


198

Python2

Para verificar se a importação pode encontrar algo no python2, use imp

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

Para encontrar importações pontilhadas, você precisa fazer mais:

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

Você também pode usar pkgutil.find_loader(mais ou menos o mesmo que a parte python3

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

Python3

Python3 ≤ 3.3

Você deve usar importlib, como eu fiz isso foi:

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

Minha expectativa é que, se você puder encontrar um carregador para ele, ele existe. Você também pode ser um pouco mais inteligente, como filtrar quais carregadores você aceitará. Por exemplo:

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

No Python3.4, os importlib.find_loader documentos python foram preteridos em favor de importlib.util.find_spec. O método recomendado é o importlib.util.find_spec. Existem outros como importlib.machinery.FileFinder, o que é útil se você estiver atrás de um arquivo específico para carregar. Descobrir como usá-los está além do escopo disso.

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

Isso também funciona com importações relativas, mas você deve fornecer o pacote inicial, para que você também possa:

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

Embora tenha certeza de que existe uma razão para fazer isso - não tenho certeza do que seria.

AVISO

Ao tentar encontrar um submódulo, ele importará o módulo pai (para todos os métodos acima)!

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

comentários bem-vindos em contornar este

Reconhecimentos

  • @rvighne for importlib
  • @ lucas-guido para python3.3 + privativo find_loader
  • @enpenax para o comportamento de pkgutils.find_loader em python2.7

3
Isso funciona apenas para módulos de nível superior, não para eggs.ham.spam.
— hemflit

3
@hemflit se você quer encontrar spamem eggs.hamvocê usariaimp.find_module('spam', ['eggs', 'ham'])
— gitaarik

5
+1, mas impé preterido em favor de importlibem Python 3.
— rvighne

4
E se o módulo importado contiver um "ImportError" real. Isso é o que estava acontecendo comigo. Então o módulo existe, mas não será "encontrado".
— enpenax

1
Após um ano, me deparei com o mesmo problema que mencionei acima e estava procurando uma solução para o Python 2: pkgutil.find_loader("my.package.module")retorna um carregador se o pacote / módulo existir e Nonese não existir . Atualize sua resposta para o Python 2, pois mascarar o ImportError me deixou louco ontem xP
— enpenax

13

Depois de usar a resposta do yarbelk, fiz isso para não precisar importar ìmp.

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

Útil no Django, settings.pypor exemplo.


4
Fiz uma votação baixa, porque isso mascara erros de importação no módulo, o que dificulta muito a identificação do erro.
— enpenax

1
O voto negativo é uma má idéia, uma boa prática é "sempre registrar erros detectados". Este é um exemplo depois de escrever como você deseja.
— Zulu

2
Como você registraria um erro se o módulo importado falha na linha 1 com um ImportError e sua tentativa de captura faz com que ele falhe silenciosamente?
— enpenax 11/11/2015

Acabei de encontrar o problema de mascarar-importar-erros na vida real e foi ruim (causando testes que deveriam estar falhando na aprovação!).
— David Given

Corri para que quando alguém estava usando esse erro para provocar uma monkeypatch em um módulo diferente ... que era loucura para encontrar
— yarbelk

13

Python 3> = 3.6: ModuleNotFoundError

O ModuleNotFoundErrorfoi introduzido no python 3.6 e pode ser usado para essa finalidade

try:
    import eggs
except ModuleNotFoundError:
    # Error handling
    pass

O erro ocorre quando um módulo ou um de seus pais não pode ser encontrado. assim

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

imprimiria uma mensagem parecida com No module named 'eggs'aeggs módulo não pode ser encontrado; mas imprimiria algo como No module named 'eggs.sub'se apenas o submódulo não pudesse ser encontrado, mas o eggspacote pudesse ser encontrado.

Consulte a documentação do sistema de importação para obter mais informações sobre oModuleNotFoundError


1
Isso não responde à pergunta porque importa o pacote, se existir
— divenex

11

Python 2, sem confiar no ImportError

Até que a resposta atual seja atualizada, aqui está o caminho para o Python 2

import pkgutil
import importlib

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

Por que outra resposta?

Muitas respostas fazem uso de pegar um ImportError. O problema disso é que não podemos saber o que lança oImportError .

Se você importar seu módulo existente e houver um ImportErrorno seu módulo (por exemplo, erro de digitação na linha 1), o resultado será que seu módulo não existe. Você precisará de uma grande quantidade de retorno para descobrir que o seu módulo existe e ImportErroré capturado e faz com que as coisas falhem silenciosamente.


Pode não ter sido claro, mas todos, exceto os primeiros blocos de código, não dependem ImportError- edite se não estiver claro para você.
— yarbelk

Vejo você usando a captura ImportError nos dois primeiros exemplos do Python 2. Por que eles estão lá, então?
— 23415 enpenax

3
Isso gera ImportError se mod == 'not_existing_package.mymodule'. Veja minha solução
— Marcin Raczyński

1
Claro que gera um erro de importação. Ele deve gerar um erro de importação se um módulo não existir. Dessa forma, você pode pegá-lo, se precisar. O problema com as outras soluções é que elas ocultam outros erros.
— enpenax

Tentar / exceto não significa que você não deve registrar ou garantir as coisas. Você pode capturar totalmente qualquer rastreio subjacente e fazer o que quiser.
— Zulu

8

A resposta de go_as como um forro

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

6

Me deparei com essa pergunta enquanto procurava uma maneira de verificar se um módulo está carregado na linha de comando e gostaria de compartilhar meus pensamentos para os que vieram depois de mim e procurando o mesmo:

Método de arquivo de script Linux / UNIX : make a file module_help.py:

#!/usr/bin/env python

help('modules')

Em seguida, verifique se é executável: chmod u+x module_help.py

E chame-o com a pipepara grep:

./module_help.py | grep module_name

Chame o sistema de ajuda interno . (Esta função é destinada ao uso interativo .) Se nenhum argumento for fornecido, o sistema de ajuda interativo será iniciado no console do intérprete. Se o argumento for uma sequência , ela será procurada como o nome de um módulo , função, classe, método, palavra-chave ou tópico da documentação, e uma página de ajuda será impressa no console. Se o argumento for qualquer outro tipo de objeto, uma página de ajuda no objeto será gerada.

Método interativo : na carga do consolepython

>>> help('module_name')

Se encontrado, saia da leitura digitando q
Para sair da sessão interativa python, pressione Ctrl+D

O método de arquivo de script do Windows também é compatível com Linux / UNIX e , em geral , melhor :

#!/usr/bin/env python

import sys

help(sys.argv[1])

Chamando a partir do comando, como:

python module_help.py site  

Saída:

Ajuda no site do módulo:

NAME site - Anexe caminhos de pesquisa do módulo para pacotes de terceiros no sys.path.

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

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

DESCRIPTION
...
:

e você teria que pressionar qpara sair do modo interativo.

Usando-o módulo desconhecido:

python module_help.py lkajshdflkahsodf

Saída:

nenhuma documentação Python encontrada para 'lkajshdflkahsodf'

e saia.


5

Use uma das funções do pkgutil , por exemplo:

from pkgutil import iter_modules

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


4

Você pode escrever um pequeno script que tentará importar todos os módulos e informar quais estão falhando e quais estão funcionando:

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

Resultado:

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

Aviso: isso tentará importar tudo, assim você verá coisas como PyYAML failed with error code: No module named pyyamlporque o nome da importação real é apenas yaml. Portanto, desde que você conheça suas importações, isso deve fazer o truque para você.


3

Eu escrevi esta função 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

Você também pode usar importlibdiretamente

import importlib

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

Isso tem o problema de realmente importá-lo. Efeitos colaterais e tudo mais
— yarbelk 09/07/19

2

Não há como verificar com segurança se o "módulo pontilhado" é importável sem importar o pacote pai. Dizendo isso, existem muitas soluções para o problema "como verificar se o módulo Python existe".

A solução abaixo aborda o problema que o módulo importado pode gerar ImportError mesmo que ele exista. Queremos distinguir essa situação daquela em que módulo não 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

no 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

Isto cumpre a minha pergunta padrão quando a programação python: WWDD (? Qual seria Django Do) e eu deveria ter o olhar lá
— yarbelk
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.