Python 2.6中的动态类加载:运行时警告:处理绝对导入时未找到父模块'plugins
我正在开发一个插件系统,插件模块是这样加载的:
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
不过这个技巧主要是为了好玩儿!