How to check if a python module exists without importing it

Python 3 >= 3.6: ModuleNotFoundError

The ModuleNotFoundError has been introduced in Python 3.6 and can be used for this purpose:

try:
    import eggs
except ModuleNotFoundError:
    # Error handling
    pass

The error is raised when a module or one of its parents cannot be found. So

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

would print a message that looks like No module named 'eggs' if the eggs module cannot be found; but it would print something like No module named 'eggs.sub' if only the sub module couldn't be found, but the eggs package could be found.

See the documentation of the import system for more information on the ModuleNotFoundError.


After using yarbelk's response, I've made this so I don't have to import ìmp.

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

It is useful in Django's settings.py file, for example.


Python2

To check if import can find something in Python 2, using imp:

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

To find dotted imports, you need to do more:

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

You can also use pkgutil.find_loader (more or less the same as the Python 3 part:

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

Python 3

Python 3 ≤ 3.3

You should use importlib. I went about doing this like:

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

My expectation being, if you can find a loader for it, then it exists. You can also be a bit more smart about it, like filtering out what loaders you will accept. For example:

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)

Python 3 ≥ 3.4

In Python 3.4 importlib.find_loader Python documentation was deprecated in favour of importlib.util.find_spec. The recommended method is the importlib.util.find_spec. There are others like importlib.machinery.FileFinder, which is useful if you're after a specific file to load. Figuring out how to use them is beyond the scope of this.

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

This also works with relative imports, but you must supply the starting package, so you could also do:

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

While I'm sure there exists a reason for doing this - I'm not sure what it would be.

Warning

When trying to find a submodule, it will import the parent module (for all of the above methods)!

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.util.find_spec("food.eggs")
module food loaded
ModuleSpec(name='food.eggs', loader=<_frozen_importlib.SourceFileLoader object at 0x10221df28>, origin='/home/user/food/eggs.py')

Comments are welcome on getting around this

Acknowledgements

  • @rvighne for importlib
  • @lucas-guido for Python 3.3+ deprecating find_loader
  • @enpenax for pkgutils.find_loader behaviour in Python 2.7