Carregamento de classe dinâmica em Python 2.6: RunTimeWarning: Módulo Parent 'Plugins' Não encontrado durante o manuseio de importação absoluta
-
20-09-2019 - |
Pergunta
Estou trabalhando em um sistema de plug -in onde os módulos de plug -in são carregados assim:
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
O código funciona, mas para cada instrução de importação no código do plug -in, recebo um aviso como este:
plugins/plugin.py:2: RuntimeWarning: Parent module 'plugins' not found while handling absolute import
import os
Nenhum erro é relatado para o código principal do programa e os plugins funcionam.
Alguém pode explicar o que o aviso significa e o que eu estou fazendo de errado. Preciso criar um módulo de plug -ins vazio separadamente e importá -lo para manter o Python feliz?
Solução
Se o diretório plugins
eram um pacote real (contido __init__.py
bem), você pode usar facilmente o PKGutils para enumerar seus arquivos de plug -in e carregá -los.
import pkgutil
# import our package
import plugins
list(pkgutil.iter_modules(plugins.__path__))
No entanto, pode funcionar sem um pacote de plug -in de qualquer maneira, tente o seguinte:
import pkgutil
list(pkgutil.iter_modules(["plugins"]))
Também é possível fazer um pacote que exista apenas em tempo de execução:
import types
import sys
plugins = types.ModuleType("plugins")
plugins.__path__ = ["plugins"]
sys.modules["plugins"] = plugins
import plugins.testplugin
No entanto, aquele hack que foi principalmente por diversão!
Outras dicas
Se o diretório de plugins não tiver um __init__.py
, não é um pacote, então quando você cria plugins.whatever
, Python adverte que isso realmente não deveria existir. (Não poderia ser criado por "import plugins.whatever
"Não importa qual seja o seu caminho.)
Também,
- Não se divida
/
, o que não é portável. Usaros.path.split
. - Não use
.split(".py")
Para obter o nome sem a extensão, que é buggy. Usaros.path.splitext
. - Não use
getattr
com uma corda literal.getattr(plugin, "__init__")
está escritoplugin.__init__
. - Estou confuso por que você está chamando de nível de módulo
__init__
função. Isso não parece certo. Talvez você queira uma função "set_logger" ou melhor, para instanciar uma classe que leva um madeireiro. - Não use
L = L + some_other_list
Para estender uma lista, use oextend
Método, que tem melhor desempenho e é mais idiomático. - Não esmaga exceções desconhecidas por
except Exception
. Se você não pode planejar fazer algo sã em resposta a uma exceção, seu programa não pode continuar a Santamente.
O problema aqui está com o ponto ('.') No nome do módulo:
imp.load_module('plugins.'+name, f, file, desc)
Não inclua um '.' Depois de 'Plugins', ou Python pensará que é um caminho do módulo.
Você pode tentar adicionar a instrução abaixo no início das instruções de importação.
from __future__ import absolute_import
O python imp
A documentação foi atualizada desde que isso foi respondido. Agora aborda esta questão especificamente no find_module()
método.
Esta função não lida com nomes de módulos hierárquicos (nomes que contêm pontos). A fim de encontrar PM, isto é, submódulo M de pacote P, usar
find_module()
eload_module()
Para encontrar e carregar pacote P, e então usefind_module()
com o caminho argumento definido comoP.__path__
. Quando P Ele próprio tem um nome pontilhado, aplique esta receita recursivamente.
Observe que P.__path__
já é uma lista ao fornecê -la para find_module()
. Observe também o que o find_module()
A documentação diz sobre encontrar pacotes.
Se o módulo for um pacote, Arquivo é
None
, Nome do caminho é o caminho do pacote e o último item no Descrição tupla éPKG_DIRECTORY
.
Então, da pergunta do OP, para importar o plugin sem o RuntimeError
Avisos, siga as instruções na documentação atualizada do 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)