Як перевірити, чи існує модуль python, не імпортуючи його


179

Мені потрібно знати, чи існує модуль python, не імпортуючи його.

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

try:
    import eggs
except ImportError:
    pass

4
Мені цікаво, які недоліки у використанні імпорту?
Чак,

15
Якщо ваш модуль має побічні ефекти, виклик імпорту може мати небажані наслідки. Отже, якщо ви хочете перевірити, яку версію файлу потрібно запустити спочатку, ви можете перевірити відповідь нижче, а імпортувати пізніше. Я не припускаю, що є гарною ідеєю писати модулі з побічними ефектами - але ми всі дорослі і можемо приймати власні рішення щодо того, наскільки небезпечно ми хочемо кодувати.
ярбельк


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-guideo для 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є застарілим на користь importlibв Python 3.
rvighne

4
Що робити, якщо імпортований модуль містить фактичний "ImportError". Ось що зі мною відбувалося. Тоді модуль існує, але не буде "знайдений".
enpenax

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

13

Після використання відповіді yarbelk я ​​зробив це, оскільки не потрібно імпортувати ìmp.

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

Корисно, наприклад, у Джанго settings.py.


4
Я підкреслив, оскільки це маскує імпорт помилок у модулі, що робить його важко помітити помилку.
enpenax

1
Downvote - це погана ідея, гарною умовою є "завжди помилки, що потрапили в журнал". Це приклад після того, як ви пишете, як вам хочеться.
Зулу

2
Як би ви зафіксували помилку, якщо імпортований модуль виходить з ладу в рядку 1 із ImportError, а ваш спроб уловлівання робить його беззвучним?
enpenax

Я просто зіткнувся з проблемою маскування-імпортування помилок у реальному житті, і це було погано (спричиняючи тести, які повинні були не пройти!).
Девід Дано

Я наткнувся на те, де хтось використовував цю помилку, щоб викликати маніпулювання на іншому модулі ... це було безумство, щоб знайти
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

Виведе:

для "lkajshdflkahsodf" не знайдено документації Python "

і вихід.


5

Використовуйте одну з функцій pkgutil , наприклад:

from pkgutil import iter_modules

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


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оскільки власне імпорт імпорту - просто ямл. Тож, поки ви знаєте, що ваш імпорт, це повинно зробити трюк для вас.


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

У цьому виникає проблема фактичного імпорту. Побічні ефекти і все
yarbelk

2

Немає можливості достовірно перевірити, чи "пунктирний модуль" імпортується, не імпортуючи його батьківський пакет. Кажучи це, існує багато рішень проблеми "як перевірити, чи існує модуль Python".

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

Пітон 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

Пітон 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.