Динамическая загрузка класса в Python 2.6:Предупреждение о времени выполнения:Родительский модуль "плагины" не найден при обработке абсолютного импорта

StackOverflow https://stackoverflow.com/questions/2267984

Вопрос

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

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

Код работает, но для каждого оператора import в коде плагина я получаю предупреждение, подобное этому:

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

Об ошибках в основном программном коде не сообщается, и плагины работают.

Кто-нибудь может объяснить, что означает это предупреждение и что я делаю не так?Нужно ли мне создавать пустой модуль plugins отдельно и импортировать его, чтобы поддерживать работоспособность python?

Это было полезно?

Решение

Если каталог 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

Однако этот хак был в основном ради развлечения!

Другие советы

Если в каталоге плагинов нет файла __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 подумает, что это путь модуля.

Вы можете попробовать добавить приведенный ниже оператор в начало операторов импорта.

from __future__ import absolute_import

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

Эта функция не обрабатывает иерархические имена модулей (имена, содержащие точки).Для того, чтобы найти П.М., то есть подмодуль 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)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top