Python 2.6中的动态类加载:运行时警告:处理绝对导入时未找到父模块'plugins

24 投票
5 回答
20943 浏览
提问于 2025-04-15 19:19

我正在开发一个插件系统,插件模块是这样加载的:

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

这段代码可以正常运行,但在插件代码中的每个导入语句旁边,我都会收到这样的警告:

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

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

有人能解释一下这个警告是什么意思吗?我哪里做错了?我需要单独创建一个空的插件模块并导入它,以让Python满意吗?

5 个回答

7

这里的问题出在模块名称中的点('.')上:

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

在'plugins'后面不要加一个点,否则Python会把它当成一个模块的路径来处理。

16

如果插件目录里没有一个叫 __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 来捕捉未知的异常。如果你无法对异常做出合理的处理,你的程序就无法正常运行。
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

不过这个技巧主要是为了好玩儿!

撰写回答