pythonpython-import

How to check if a Python module exists without importing it


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

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