Python 2.6中的动态类加载:RuntimeWarning:处理绝对导入时未找到父模块“插件”

时间:2010-02-15 18:13:26

标签: python plugins import dynamic-class-loaders

我正在开发一个插件系统,插件模块的加载方式如下:

def load_plugins():
   plugins=glob.glob("plugins/*.py")
   instances=[]
   for p in plugins:
      try:
         name=p.split("/")[-1]
         name=name.split(".py")[0]
         log.debug("Possible plugin: %s", name)
         f, file, desc=imp.find_module(name, ["plugins"])
         plugin=imp.load_module('plugins.'+name, f, file, desc)
         getattr(plugin, "__init__")(log)
         instances=instances+plugin.get_instances()
      except Exception as e:
         log.info("Failed to load plugin: "+str(p))
         log.info("Error: %s " % (e))
         log.info(traceback.format_exc(e))
   return instances

代码有效,但对于插件代码中的每个import语句,我都会收到如下警告:

plugins/plugin.py:2: RuntimeWarning: Parent module 'plugins' not found while handling absolute import
  import os

没有报告主程序代码的错误,插件也能正常工作。

有人可以解释警告意味着什么以及我做错了什么。我是否需要单独创建一个空的插件模块并导入它以保持python的快乐?

5 个答案:

答案 0 :(得分:16)

如果plugins目录没有__init__.py,那么它不是包,所以当你创建plugins.whatever时,Python会警告你这样的事情不应该存在。 (无论您的路径是什么,都无法通过“import plugins.whatever”创建。)

此外,

  • 不要拆分/,这是不可移植的。使用os.path.split
  • 不要使用.split(".py")来获取没有扩展名的名称,这是错误的。使用os.path.splitext
  • 不要将getattr与字符串文字一起使用。 getattr(plugin, "__init__")的拼写为plugin.__init__
  • 我很困惑为什么要调用模块级__init__函数。这似乎不对。也许您想要一个“set_logger”函数或更好的函数来实例化一个带记录器的类。
  • 请勿使用L = L + some_other_list扩展列表,请使用extend方法,该方法具有更好的性能且更具惯用性。
  • 请勿按except Exception压缩未知的例外情况。如果你不能计划在异常情况下做一些理智的事情,那么你的计划就无法顺利进行。

答案 1 :(得分:14)

如果目录plugins是真正的包(包含__init__.py),您可以轻松使用pkgutils枚举其插件文件并加载它们。

import pkgutil
# import our package
import plugins
list(pkgutil.iter_modules(plugins.__path__))

但是,无论如何它都可以在没有插件包的情况下工作,试试这个:

import pkgutil
list(pkgutil.iter_modules(["plugins"]))

还可以创建仅在运行时存在的包:

import types
import sys
plugins = types.ModuleType("plugins")
plugins.__path__ = ["plugins"]

sys.modules["plugins"] = plugins
import plugins.testplugin

然而,黑客主要是为了好玩!

答案 2 :(得分:6)

这里的问题是模块名称中的点('。'):

imp.load_module('plugins.'+name, f, file, desc)

不要包含'。'在“插件”之后,或者Python会认为它是一个模块路径。

答案 3 :(得分:2)

您可以尝试在import语句的开头添加以下语句。

from __future__ import absolute_import

答案 4 :(得分:0)

Python imp文档已经更新,因为已经得到了解答。它现在特别在find_module()方法中解决了这个问题。

  

此功能不处理分层模块名称(包含点的名称)。要查找 PM ,即包 P 的子模块 M ,请使用find_module()load_module()查找并加载包 P ,然后使用find_module()并将路径参数设置为P.__path__。当 P 本身具有虚线名称时,请递归应用此配方。

请注意,P.__path__在将其提供给find_module()时已经是一个列表。另请注意find_module()文档中有关查找包的内容。

  

如果模块是包,文件None路径名是包路径和描述中的最后一项元组是PKG_DIRECTORY

从OP的问题来看,要导入没有RuntimeError警告的插件,请按照更新的Python文档中的说明进行操作:

# first find and load the package assuming it is in
# the current working directory, '.'

f, file, desc = imp.find_module('plugins', ['.'])
pkg = imp.load_module('plugins', f, file, desc)

# then find the named plugin module using pkg.__path__
# and load the module using the dotted name

f, file, desc = imp.find_module(name, pkg.__path__)
plugin = imp.load_module('plugins.' + name, f, file, desc)