How to check if a python module exists without importing it

yarbelk picture yarbelk · Dec 27, 2012 · Viewed 176.5k times · Source

I need to know if a python module exists, without importing it.

Importing something that might not exist (not what I want):

try:
    import eggs
except ImportError:
    pass

Answer

yarbelk picture yarbelk · Dec 27, 2012

Python2

To check if import can find something in python2, 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 python3 part

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

Python3

Python3 ≤ 3.3

You should use importlib, How I went about doing this was:

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)

Python3 ≥ 3.4

In Python3.4 importlib.find_loader python docs 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.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 welcome on getting around this

Acknowledgements

  • @rvighne for importlib
  • @lucas-guido for python3.3+ depricating find_loader
  • @enpenax for pkgutils.find_loader behaviour in python2.7