How to check if a Python module exists without importing it
python, python-import
Solution
TL;DR) Use `importlib.util.find_spec(module_name)` (Python 3.4+).
Python2: `imp.find_module`
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: `importlib.find_loader`
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: `importlib.util.find_spec`
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.util
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.util
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
Problem
How can I know if a Python module exists, without importing it? Importing something that might not exist (not what I want) results in: ``` try: import eggs except ImportError: pass ```