【问题标题】:How to import namespaced packages for which sys.path needs to be adjusted?如何导入需要调整 sys.path 的命名空间包?
【发布时间】:2012-04-03 06:20:48
【问题描述】:

在我正在开发的 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”行初始化了插件命名空间/包,并且没有进一步尝试在 sys.path 中搜索命名空间中的其他包。

如果我把代码改成这样:

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

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

所以,我的问题是:

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

【问题讨论】:

  • 你真的不需要弄乱 sys.path 。只需使用软件包。对于动态导入,您可以调用 __import__ 函数。或者使用imp 模块。
  • 如果我的操作正确,为什么不直接将plugins 制作成一个包并将plugins/lib1plugins/lib2 添加到路径中,然后将import fooimport bar 添加到路径中?
  • 也就是说,我认为您真正的问题是如何在 Python 中制作插件系统 - 请参阅:stackoverflow.com/questions/7417997/plugin-manager-in-python/…

标签: python python-2.6 import


【解决方案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 上,sys.modules 字典中的旧“balla”条目被缓存并保持不变。这会导致经典错误消息:

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

通过从sys.modules 中删除“balla”包的缓存条目,可以解决问题:

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 子句。

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 我不熟悉重新加载,这解决了我的问题。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多