Загрузка динамического класса в 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 счастливым?
Ответы
Ответ 1
Если каталог 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
Однако этот хак был в основном для удовольствия!
Ответ 2
Если каталог плагинов не имеет __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
. Если вы не можете планировать что-то здравомыслящее в ответ на исключение, ваша программа не может идти здорово.
Ответ 3
Проблема здесь в том, что точка ('.') в имени модуля:
imp.load_module('plugins.'+name, f, file, desc)
Не включайте '.' после "плагинов", или Python будет считать это путь к модулю.
Ответ 4
Документация 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)
Ответ 5
Вы можете попробовать добавить инструкцию ниже в начале операторов импорта.
from __future__ import absolute_import