Динамическая загрузка класса в Python 2.6:Предупреждение о времени выполнения:Родительский модуль "плагины" не найден при обработке абсолютного импорта
-
20-09-2019 - |
Вопрос
Я работаю над системой плагинов, где модули плагинов загружаются следующим образом:
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)