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

zulu
fonte
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

J.Baoby
fonte
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.

enpenax
fonte
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

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

go_as
fonte
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())
ArtOfWarfare
fonte
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ê.

Usuário9123
fonte
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
Fardin
fonte
2

Você também pode usar importlibdiretamente

import importlib

try:
    importlib.import_module(module_name)
except ImportError:
    # Handle error
jackotonye
fonte
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
Marcin Raczyński
fonte
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
dibrovsd
fonte
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