【问题标题】:Dynamic class loading in Python 2.6: RuntimeWarning: Parent module 'plugins' not found while handling absolute importPython 2.6 中的动态类加载:RuntimeWarning:处理绝对导入时未找到父模块“插件”
【发布时间】:2011-01-17 02:25:26
【问题描述】:

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

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

【问题讨论】:

  • 备案:'plugins' in Parent module 'plugins' not found 来自传递给imp.load_modulename 值,例如。 "plugins.something"imp.load_module("plugins.something")。在我的情况下,name 的值类似于".something",因此消息包含'' 而不是'plugins'

标签: python plugins import dynamic-class-loaders


【解决方案1】:

如果 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 压制未知异常。如果您无法计划对异常做出理智的反应,那么您的程序就无法理智地进行。

【讨论】:

  • 请注意,插件目录不是一个包,也不是导入的(我想从中挑选文件)。其余代码编译为鸡蛋,直接使用“python -m”运行。我从不将插件目录添加到 sys.path。
  • 对,但是如果你有一个插件foo,你告诉它它是plugins.foo,所以它认为它是plugins 包的一部分(它不存在)。当在 foo.pyimport os(或者如果你有 imported anythingelse)内时,你会收到该警告,因为 plugins 并不真正存在。
  • (这意味着您可以选择忽略警告、更改命名方案或更改结构以匹配 Python 的预期。)
  • except Exception 不会捕获所有异常,尤其是 SystemExit 或 KeyboardInterrupt。 Sincie Py 2.6 也不是 GeneratorExit。
  • 我想我明白了,谢谢你的解释。感谢您对我的通用 Python 的帮助。掌握 API 并知道应该如何做需要一段时间。编辑:Mike 的 Ninja 帖子:有没有办法创建一个空包?
【解决方案2】:

如果目录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

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

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

      【解决方案4】:

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

      from __future__ import absolute_import
      

      【讨论】:

        【解决方案5】:

        Python imp 文档在得到答复后已更新。它现在专门在find_module() 方法中解决了这个问题。

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

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

        如果模块是包,fileNonepathname是包路径,description元组的最后一项是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)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-08-01
          • 1970-01-01
          • 2020-05-06
          • 2018-06-01
          相关资源
          最近更新 更多