3

I have a few modules in python, which are imported dynamicly and all have the same structur (plugin.py, models.py, tests.py, ...). In the managing code i want to import those submodules, but for example models.py or tests.py is not mandatory. (So i could have plugin_a.plugin and plugin_a.tests but only plugin_b.plugin).

I can check if the submodule exists by

try:
    __import__(module_name + ".tests")
except ImportError:
    pass

That will fail, if module_name+".tests" is not found, but it will also fail if the tests-module itself will try to import something, which is not found, for example because of a typo. Is there any way to check if the module exists, without importing it or make sure, the ImportError is only raised by one specific import-action?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Daishy
  • 238
  • 2
  • 11

4 Answers4

3

You can see from the length of the traceback how many levels deep the import failed. A missing .test module has a traceback with just one frame, a direct dependency failing has two frames, etc.

Python 2 version, using sys.exc_info() to access the traceback:

import sys


try:
    __import__(module_name + ".tests")
except ImportError:
    if sys.exc_info()[-1].tb_next is not None:
        print "Dependency import failed"
    else:
        print "No module {}.tests".format(module_name)

Python 3 version, where exceptions have a __traceback__ attribute:

try:
    __import__(module_name + ".tests")
except ImportError as exc:
    if exc.__traceback__.tb_next is not None:
        print("Dependency import failed")
    else:
        print("No module {}.tests".format(module_name))

Demo:

>>> import sys
>>> def direct_import_failure(name):
...     try:
...         __import__(name)
...     except ImportError:
...         return sys.exc_info()[-1].tb_next is None
... 
>>> with open('foo.py', 'w') as foo:
...     foo.write("""\
... import bar
... """)
... 
>>> direct_import_failure('bar')
True
>>> direct_import_failure('foo')
False
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
2

Following on from: How to check if a python module exists without importing it

The imp.find_module function will either return a 3-element tuple (file, pathname, description) or raise an ImportError.

It will not raise an error if there is a problem with the module, only if it doesn't exist.

The python docs suggest that you should first find and import the package and then use its path in second find_module, doing this recursively if necessary.

This seems a little messy to me.

The function below will check for the existence of a given module, at any level of relative import (module.py, package.module.py, package.subpackage.module.py etc.).

imp.find_module returns an open file, which you could use in imp.load_module, but this, too seems a little clunky, so I close the file so that it can be imported outside of the function.

Note that this isn't perfect. if you are looking for package.subpackage.module but actually package.subpackage is a valid module it will also return true.

import imp
import importlib

def module_exists(modulename):
    modlist = modulename.split('.')
    pathlist = None
    for mod in modlist:
        print mod
        try:
            openfile, pathname, desc = imp.find_module(mod,pathlist)
            pathlist = [pathname]
        except ImportError:
            print "Module '{}' does not exist".format(mod)
            return(False)
        else:
            print 'found {}'.format(openfile)
            if openfile:
                openfile.close()
                return(True)

if __name__ == '__main__':
    mymodule = 'parrot.type.norwegian_blue'
    if module_exists(mymodule):
        importlib.import_module(mymodule)

Note also that I'm using importlib.import_module instead of __import__.

Finally, note that importlib is Python 2.7 and upwards

Community
  • 1
  • 1
SiHa
  • 7,830
  • 13
  • 34
  • 43
1

You know what the import error message will look like if the module doesn't exist so just check for that:

try:
    module = module_name + '.tests'
    __import__(module)
except ImportError, e:
    if e.args and e.args[0] == 'No module named ' + module:
        print(module, 'does not exist')
    else:
        print(module, 'failed to import')
tdelaney
  • 73,364
  • 6
  • 83
  • 116
  • Thanks, that worked. I'm a little bit concerned about depending on the exact wording of the error-message, but maybee i just check if the module is in the error-string:) – Daishy May 18 '13 at 18:09
  • 1
    @Daishy - yeah, could result in non-obvious failure later. Just account for submodules. module.tests.froob contains module.tests but shouldn't match your test. – tdelaney May 18 '13 at 18:45
1

Is there any way to check if the module exists, without importing it or make sure, the ImportError is only raised by one specific import-action?

There could be multiple reasons why ImportError fails because importing will evaluate the module; if there is a syntax error the module will fail to load.

To check if a module exists without loading it, use pkgutil.find_loader, like this:

>>> pkgutil.find_loader('requests')
<pkgutil.ImpLoader instance at 0x9a9ce8c>
>>> pkgutil.find_loader('foo')

It will return either a ImpLoader instance, or None if the package is not importable. You can get further details from the ImpLoader instance, like the path of the module:

>>> pkgutil.find_loader('django').filename
'/usr/local/lib/python2.7/dist-packages/django'
Burhan Khalid
  • 169,990
  • 18
  • 245
  • 284
  • Nice, although this still isn't a general solution because it doesn't support relative imports. (`ImportError: Relative module names not supported`) – Chris Martin Jun 17 '14 at 08:32