Загрузка динамического класса в Python 2.6: RuntimeWarning: плагины модуля родителя не найдены при обработке абсолютного импорта

Я работаю над плагиновой системой, где модули модулей загружаются следующим образом:

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 ответа

Если каталог plugins был реальным пакетом (содержал __init__.py fine), вы могли бы легко использовать 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

Однако этот хак был в основном для удовольствия!


Если каталог плагинов не имеет __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. Если вы не можете планировать что-то здравомыслящее в ответ на исключение, ваша программа не может идти здорово.


Проблема здесь в том, что точка ('.') в имени модуля:

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

Не включайте '.' после "плагинов", или Python будет считать это путь к модулю.


Документация Python imp обновлена ​​с момента ответа. Теперь этот вопрос рассматривается именно в методе find_module().

Эта функция не обрабатывает имена иерархических модулей (имена, содержащие точки). Чтобы найти PM, то есть подмодуль M пакета P, используйте 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)

licensed under cc by-sa 3.0 with attribution.