【问题标题】:Distinguish between ImportError because of not found module or faulty import in module itself in python?区分 ImportError 是因为在 python 中找不到模块还是模块本身的错误导入?
【发布时间】:2013-05-13 14:48:48
【问题描述】:

我在 python 中有一些模块,它们是动态导入的,并且都具有相同的结构(plugin.py、models.py、tests.py、...)。在管理代码中,我想导入这些子模块,但例如 models.py 或 tests.py 不是强制性的。 (所以我可以有plugin_a.pluginplugin_a.tests 但只有plugin_b.plugin)。

我可以通过以下方式检查子模块是否存在

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

如果没有找到module_name+".tests",这将失败,但如果tests-模块本身尝试导入一些未找到的东西,例如由于拼写错误,它也会失败。 有什么方法可以检查模块是否存在,而无需导入或确保 ImportError 仅由一个特定的导入操作引发?

【问题讨论】:

    标签: python import module importerror


    【解决方案1】:

    如果模块不存在,您知道导入错误消息会是什么样子,因此只需检查一下:

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

    【讨论】:

    • 谢谢,成功了。我有点担心取决于错误消息的确切措辞,但也许我只是检查模块是否在错误字符串中:)
    • @Daishy - 是的,以后可能会导致不明显的失败。只需考虑子模块。 module.tests.froob 包含 module.tests 但不应与您的测试匹配。
    【解决方案2】:

    有什么方法可以在不导入的情况下检查模块是否存在 或确保 ImportError 仅由一个特定的 导入操作?

    ImportError 失败可能有多种原因,因为导入会评估模块;如果存在语法错误,模块将无法加载。

    要检查模块是否存在而不加载它,请使用pkgutil.find_loader,如下所示:

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

    如果包不可导入,它将返回ImpLoader 实例或None。您可以从ImpLoader 实例中获取更多详细信息,例如模块的路径:

    >>> pkgutil.find_loader('django').filename
    '/usr/local/lib/python2.7/dist-packages/django'
    

    【讨论】:

    • 很好,虽然这仍然不是一个通用的解决方案,因为它不支持相对导入。 (ImportError: Relative module names not supported)
    【解决方案3】:

    接下来来自:How to check if a python module exists without importing it

    imp.find_module 函数将返回一个 3 元素元组(文件、路径名、描述)或引发 ImportError。

    如果模块有问题,它不会引发错误,只有当它不存在时。

    python 文档建议您首先找到并导入包,然后在第二个find_module 中使用它的路径,必要时递归执行此操作。

    这对我来说似乎有点混乱。

    下面的函数将检查给定模块是否存在,在任何相对导入级别(module.pypackage.module.pypackage.subpackage.module.py 等)。

    imp.find_module 返回一个打开的文件,您可以在imp.load_module 中使用该文件,但这似乎也有点笨拙,所以我关闭了该文件,以便可以将其导入函数之外。

    请注意,这并不完美。如果您正在寻找 package.subpackage.module 但实际上 package.subpackage 是一个有效模块,它也会返回 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)
    

    另请注意,我使用的是importlib.import_module 而不是__import__

    最后,请注意importlib 是 Python 2.7 及更高版本

    【讨论】:

    • 更新代码以更好地支持任何级别的相对导入。
    【解决方案4】:

    您可以从回溯的长度看到导入失败的深度。缺少的.test 模块只有一帧的回溯,直接依赖失败有两帧,等等。

    Python 2 版本,使用sys.exc_info() 访问回溯:

    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 版本,其中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))
    

    演示:

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

    【讨论】:

    • 可爱。这似乎是迄今为止最灵活、最可靠的答案。
    猜你喜欢
    • 1970-01-01
    • 2017-03-16
    • 1970-01-01
    • 2012-10-04
    • 1970-01-01
    • 2021-10-20
    • 1970-01-01
    • 2010-10-17
    相关资源
    最近更新 更多