24

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

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 快乐?

4

5 回答 5

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. 如果你不能计划做一些理智的事情来响应异常,你的程序就不能理智地继续下去。
于 2010-02-15T18:16:31.680 回答
14

If the directory plugins were a real package (contained __init__.py fine), you could easily use pkgutils to enumerate its plugin files and load them.

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

However, it can work without a plugin package anyway, try this:

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

Also it is possible to make a package that only exists at runtime:

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

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

However that hack that was mostly for fun!

于 2010-02-15T18:54:37.803 回答
7

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

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

不要包含“。” 在 'plugins' 之后,否则 Python 会认为它是一个模块路径。

于 2014-10-18T08:08:08.590 回答
2

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

from __future__ import absolute_import
于 2018-12-07T10:22:33.950 回答
0

自从回答了这个问题后,Pythonimp文档已经更新。find_module()它现在在方法中专门解决了这个问题。

此函数不处理分层模块名称(包含点的名称)。为了找到PM,即包P的子模块M,使用and来查找并加载包P,然后将路径参数设置为。当P本身有一个带点的名称时,递归地应用这个配方。find_module()load_module()find_module()P.__path__

请注意,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)
于 2016-09-22T09:42:00.660 回答