如何导入需要调整sys.path的命名空间包?

时间:2012-03-18 13:35:35

标签: python python-2.6 import

在我正在研究的python应用程序中,我想根据运行时提供的信息(例如从配置文件)动态加载包(插件)。

所以,我改变sys.path来添加我想要加载的插件的路径,这通常可以正常工作,但是如果插件是一个已经初始化的命名空间中的命名空间包(请假设这是问题)反正)。

示例:

# lib1 contains plugins/__init__.py and plugins/foo/__init__.py
# lib2 contains plugins/__init__.py and plugins/bar/__init__.py
# plugins is a namespace package

import sys

sys.path.append ('lib1')
import plugins.foo

sys.path.append ('lib2')
import plugins.bar

上面的代码因ImportError而失败,可能是因为“import plugins.foo”行初始化了plugins命名空间/包,并且没有进一步尝试在sys.path中搜索命名空间中的其他包。

如果我将代码更改为:

sys.path.append ('lib1')
sys.path.append ('lib2')
import plugins.foo
import plugins.bar

两个导入都有效,但我想在plugins.foo导入后添加到sys.path。

所以,我的问题是:

  1. 我的假设是否正确,因为第二次导入失败了 命名空间包?
  2. 有解决方法吗?

2 个答案:

答案 0 :(得分:1)

我认为你的诊断是正确的。导入plugins.foo时,python还会加载plugins并将其缓存在sys.modules中。我没有复制您的设置,但在导入del sys.modules['plugins']之前我会尝试bar。如果这不起作用,请尝试重新加载plugins

sys.path.append ('lib2')
import plugins
reload(plugins)
import plugins.bar

我必须承认,通过协议,重新加载并不是运行程序的好主意。 “正确”的解决方案是找出一个工作流程,让您在开始从包中导入之前设置完整的sys.path。或者也许不将一个包裹分散到不同的地方。

答案 1 :(得分:1)

这不是答案,而是扩展评论。

上述原始问题中给出的导入情况并不仅仅在尝试导入两个不同的插件时出现。在我们的案例中,我们正在尝试为当前系统中未安装所需Python包的情况提供本地回退。

MVE的整体项目结构可能如下所示:

.
|-- src
|   `-- balla
|       |-- __init__.py
|       `-- hurga
|           `-- __init__.py
|-- src2
|   `-- balla
|       |-- __init__.py
|       `-- hurga2
|           `-- __init__.py
`-- test.py

,其中src/balla/__init__.pysrc2/balla/__init__.py具有相同的源代码:

#!/usr/bin/env python
try:
    import pkg_resources
    pkg_resources.declare_namespace(__name__)
except ImportError:
    from pkgutil import extend_path
    __path__ = extend_path(__path__, __name__)

。目前,这两个src/balla/hurga/__init__.pysrc2/balla/hurga2/__init__.py都是空的。在我们的主要文字test.py中:

import sys, os

sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), 'src'))

try:
    import balla.hurga2
except:
    sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), 'src2'))
    import balla.hurga2

我们首先尝试导入包" balla.hurga2"从系统直接,希望有人正确安装它(例如通过pip)。与附加sys.path.append文件夹相关的第一个src,尝试模拟具有公共命名空间的其他包的事实" balla"已安装(例如balla.extended)。但是balla.hurga2不存在,所以声明命名空间" balla"是成功的,但实际的进口不是。在except路径中,我们现在尝试通过将本地路径插入我们的回退包" src2"来解决这种情况。在新的import balla.hurga2旧的" balla" sys.modules dict中的条目被缓存并保持不变。这导致了经典的错误消息:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    import balla.hurga2
ImportError: No module named hurga2

删除&#34; balla&#34;的缓存条目来自sys.modules的包可以解决问题:

import sys, os

sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), 'src'))

try:
    import balla.hurga2
except:
    if 'balla' in sys.modules:
        del sys.modules['balla']
    sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), 'src2'))
    import balla.hurga2

对于根本没有balla包的系统,需要额外的if子句。