Distinguish between ImportError because of not found module or faulty import in module itself in python?

和自甴很熟 提交于 2019-12-04 16:03:25

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')

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
SiHa

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

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'
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!