Как проверить, существует ли модуль Python без его импорта?


179

Мне нужно знать, существует ли модуль Python, не импортируя его.

Импортировать то, что может не существовать (не то, что я хочу):

try:
    import eggs
except ImportError:
    pass

4
Мне интересно, каковы недостатки использования импорта?
Чак

15
Если у вашего модуля есть побочные эффекты, вызов import может иметь нежелательные последствия. Таким образом, если вы хотите проверить, какая версия файла должна выполняться в первую очередь, вы можете проверить ответ ниже и выполнить импорт позже. Я не утверждаю, что это хорошая идея - писать модули с побочными эффектами - но мы все взрослые люди и можем сами принимать решения о том, насколько опасно мы хотим кодировать.
Ярбельк


1
@ArtOfWarfare Я только что закрыл вопрос, который вы связали, как дубликат этого . Потому что этот вопрос понятнее, а также предложенное здесь решение лучше, чем все остальные, перечисленные здесь. Я бы предпочел указать тому, кто хочет получить ответ на это лучшее решение, чем отводить людей от него.
Бакуриу

7
@Chuck Кроме того, модуль может существовать, но сам может содержать ошибки импорта. Перехват ImportErrors, как в приведенном выше коде, может указывать на то, что модуль не существует, если он существует, но имеет ошибки.
Майкл Бартон

Ответы:


199

python2

Чтобы проверить, может ли импорт найти что-то в python2, используйте imp

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

Чтобы найти точечный импорт, вам нужно сделать больше:

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

Вы также можете использовать pkgutil.find_loader(более или менее так же, как часть Python3

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

python3

Python3 ≤ 3.3

Вы должны использовать importlib, как я делал это было:

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

Я ожидаю, что если вы можете найти загрузчик для этого, то он существует. Вы также можете быть немного умнее, например, отфильтровывая, какие загрузчики вы примете. Например:

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

В Python3.4 importlib.find_loader документы на python устарели в пользу importlib.util.find_spec. Рекомендуемый метод - importlib.util.find_spec. Есть и другие подобные importlib.machinery.FileFinder, которые полезны, если вы хотите загрузить определенный файл. Выяснить, как их использовать, выходит за рамки этого.

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

Это также работает с относительным импортом, но вы должны предоставить начальный пакет, так что вы также можете сделать:

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

Хотя я уверен, что есть причина для этого - я не уверен, что это будет.

ПРЕДУПРЕЖДЕНИЕ

При попытке найти подмодуль, он импортирует родительский модуль (для всех вышеперечисленных методов)!

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

комментарии добро пожаловать на это

Подтверждения

  • @rvighne для importlib
  • @ lucas-guido для python3.3 + с указанием стоимости find_loader
  • @enpenax для поведения pkgutils.find_loader в python2.7

3
Это работает только для модулей верхнего уровня, а не для eggs.ham.spam.
Hemflit

3
@hemflit если вы хотите найти spamв eggs.hamвы будете использоватьimp.find_module('spam', ['eggs', 'ham'])
gitaarik

5
+1, но impне рекомендуется в пользу importlibPython 3.
rvighne

4
Что если импортированный модуль содержит фактическую «ImportError». Это то, что происходило со мной. Тогда модуль существует, но не будет «найден».
enpenax

1
Через год я столкнулся с той же проблемой, о которой упоминал выше, и начал искать решение для Python 2: pkgutil.find_loader("my.package.module")возвращает загрузчик, если пакет / модуль существует, а Noneесли нет. Пожалуйста, обновите свой ответ для Python 2, так как маскировка ImportError свела меня с ума вчера xP
enpenax

13

После использования ответа Ярбелк я сделал это, так как не нужно импортировать ìmp.

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

Полезно, settings.pyнапример, в Django .


4
Я понизил голосование, потому что это маскирует ошибки импорта в модуле, что сильно затрудняет обнаружение ошибки.
enpenax

1
Downvote - плохая идея, хорошая практика - «всегда регистрируйся в журнале ошибок». Это пример после того, как вы напишите, как вы этого хотите.
Зулу

2
Как вы будете регистрировать ошибку, если импортированный модуль завершится с ошибкой в ​​строке 1 с помощью ImportError, и ваш try-улов заставит его работать молча?
enpenax

Я только что столкнулся с проблемой маскировки-импорта-ошибок в реальной жизни, и это было плохо (вызывая тесты, которые не должны были пройти!).
Дэвид Дан

Я побежал в том , что когда кто - то использовал эту ошибку , чтобы вызвать monkeypatch на другом модуле ... это было безумие , чтобы найти
yarbelk

13

Python 3> = 3.6: ModuleNotFoundError

Он ModuleNotFoundErrorбыл введен в Python 3.6 и может быть использован для этой цели

try:
    import eggs
except ModuleNotFoundError:
    # Error handling
    pass

Ошибка возникает, когда модуль или один из его родителей не может быть найден. Так

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

напечатает сообщение, которое выглядит, No module named 'eggs'если eggsмодуль не может быть найден; но напечатает что-то вроде, No module named 'eggs.sub'если только subмодуль не может быть найден, но eggsпакет может быть найден.

См. Документацию системы импорта для получения дополнительной информации оModuleNotFoundError


1
Это не отвечает на вопрос, потому что он импортирует пакет, если он существует
divenex,

11

Python 2, не полагаясь на ImportError

Пока текущий ответ не обновлен, вот путь для Python 2

import pkgutil
import importlib

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

Почему другой ответ?

Многие ответы используют ловлю ImportError. Проблема в том, что мы не можем знать, что бросает ImportError.

Если вы импортируете свой существующий модуль и ImportErrorв вашем модуле окажется (например, опечатка в строке 1), результатом будет то, что ваш модуль не существует. Для того, чтобы выяснить, что ваш модуль существует, ImportErrorон пойман и заставляет вещи молча терпеть неудачу.


Возможно, это было неясно, но на все блоки кода, кроме первых, не полагались ImportError- пожалуйста, отредактируйте, если это было неясно для вас.
Ярбельк

Я вижу, как вы используете улов ImportError в первых двух примерах Python 2. Тогда почему они там?
enpenax

3
Это выдает ImportError, если mod == 'not_existing_package.mymodule'. Смотрите мое полное решение ниже
Marcin Raczyński

1
Конечно, это вызывает ошибку импорта. Предполагается, что выдается ошибка импорта, если модуль не существует. Таким образом, вы можете поймать его, если вам нужно. Проблема с другими решениями заключается в том, что они маскируют другие ошибки.
enpenax

Попытка / исключение не означает, что вы не должны входить в систему или гарантировать вещи. Вы можете полностью перехватить любой основной след и сделать все, что захотите.
Зулу

8

Ответ go_as как один вкладыш

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

6

Я столкнулся с этим вопросом, когда искал способ проверить, загружен ли модуль из командной строки, и хотел бы поделиться своими мыслями о тех, кто придет после меня и ищет то же самое:

Метод файла сценария Linux / UNIX : создать файл module_help.py:

#!/usr/bin/env python

help('modules')

Затем убедитесь, что это исполняемый файл: chmod u+x module_help.py

И назвать это с pipeк grep:

./module_help.py | grep module_name

Вызвать встроенную справочную систему . (Эта функция предназначена для интерактивного использования .) Если аргумент не указан, интерактивная справочная система запускается на консоли переводчика. Если аргумент является строкой , то строка ищется как имя модуля , функции, класса, метода, ключевого слова или темы документации, а страница справки выводится на консоль. Если аргумент является любым другим типом объекта, генерируется страница справки об объекте.

Интерактивный метод : в консоли загрузитьpython

>>> help('module_name')

Если найдено, q
выйдите из чтения, набрав Для выхода из интерактивного сеанса python нажмите Ctrl+D

Метод файла сценария Windows также совместим с Linux / UNIX, и в целом лучше :

#!/usr/bin/env python

import sys

help(sys.argv[1])

Вызов это из команды, как:

python module_help.py site  

Будет вывод:

Помощь по модулю сайта:

NAME site - Добавьте пути поиска модулей для сторонних пакетов в sys.path.

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

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

DESCRIPTION
...
:

и вам придется нажать, qчтобы выйти из интерактивного режима.

Используем неизвестный модуль:

python module_help.py lkajshdflkahsodf

Будет вывод:

не найдена документация Python для 'lkajshdflkahsodf'

и выход.




4

Вы можете написать небольшой скрипт, который попытается импортировать все модули и сказать, какие из них работают с ошибками, а какие работают:

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

Вывод:

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

Слово предупреждения, это попытается импортировать все, так что вы увидите такие вещи, PyYAML failed with error code: No module named pyyamlпотому что фактическое имя импорта просто yaml. Так что, пока вы знаете свой импорт, это должно помочь вам.


3

Я написал эту вспомогательную функцию:

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

Вы также можете использовать importlibнапрямую

import importlib

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

Это проблема его фактического импорта. Побочные эффекты и все
ярбельк

2

Невозможно надежно проверить, можно ли импортировать «пунктирный модуль» без импорта его родительского пакета. Сказав это, есть много решений проблемы "как проверить, существует ли модуль Python".

Ниже приведено решение проблемы, при которой импортированный модуль может вызвать ImportError, даже если он существует. Мы хотим отличить ту ситуацию от такой, в которой модуль не существует.

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

в 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

Это отвечает моему стандартному вопросу при программировании на python: WWDD (Что бы сделал Django?), И мне следовало бы посмотреть туда
yarbelk
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.