从python 2到python 3的模块导入问题

时间:2020-03-06 01:16:20

标签: python python-importlib python-2to3 imp

我正在尝试将一个我没有编写的10年事件监听器从Python 2.7升级到python 3.7。我遇到的一个基本问题是原始脚本导入其插件的方式。原始脚本的思想是,将任何python文件放入“插件”文件夹中,其中的 中带有“ registerCallbacks”函数,它将自动将其自身加载到事件侦听器中并运行。多年来,它在许多工作室中一直运行良好,但是Python 3.7完全不喜欢它。
原始代码的文件夹结构如下:

EventListenerPackage
    src
        event_listener.py
    plugins
        plugin_1.py
        plugin_2.py

由此可见,事件监听器和插件都保存在彼此平行的文件夹中,而不是嵌套的。
原始代码如下所示:

# Python 2.7 implementation

import imp

class Plugin(object):
    def __init__(self, path):
        self._path = 'c:/full/path/to/EventListenerPackage/plugins/plugin_1.py'
        self._pluginName = 'plugin_1'

    def load(self):
        try:
            plugin = imp.load_source(self._pluginName, self._path)
        except:
            self._active = False
            self.logger.error('Could not load the plugin at %s.\n\n%s', self._path, traceback.format_exc())
            return

        regFunc = getattr(plugin, 'registerCallbacks', None)

由于Python 3导入模块的方式发生了变化(据我所知),其他消息板似乎都无法帮助我。
我尝试了几种不同的方法,到目前为止最好的方法是:
How to import a module given the full path?
我尝试了几种不同的方法,包括将完整路径添加到sys.path,但是我总是收到“ ModuleNotFoundError”。
这是我现在的大致位置。

import importlib.util
import importlib.abc
import importlib

class Plugin(object):
    def __init__(self, path):
        self._path = 'c:/full/path/to/EventListenerPackage/plugins/plugin_1.py'
        self._pluginName = 'plugin_1'

    def load(self):
        try:
            spec = importlib.util.spec_from_file_location('plugins.%s' % self._pluginName, self._path)
            plugin = importlib.util.module_from_spec(spec)
            # OR I HAVE ALSO TRIED
            plugin = importlib.import_module(self._path)
        except:
            self._active = False
            self.logger.error('Could not load the plugin at %s.\n\n%s', self._path, traceback.format_exc())
            return

        regFunc = getattr(plugin, 'registerCallbacks', None)

有人对我如何真正使用给定的文件夹结构导入这些模块有任何见解吗? 预先感谢。

1 个答案:

答案 0 :(得分:0)

您正在像对待plugins一样对待它。不是。这只是一个文件夹,您恰好在其中插入了插件源代码。

您需要停止将plugins.放在模块spec_from_file_location中的模块名称参数前面:

spec = importlib.util.spec_from_file_location(self._pluginName, self._path)

除此之外,您还缺少实际执行模块代码的部分:

spec.loader.exec_module(plugin)

根据您希望插件系统与常规模块进行交互的方式,您可以选择将插件目录粘贴到导入路径上:

sys.path.append(plugin_directory)

,然后使用importimportlib.import_module导入您的插件。可能是importlib.import_module,因为听起来插件加载程序似乎无法提前知道插件名称:

plugin = importlib.import_module(plugin_name)

如果这样做,插件将被视为普通模块,其后果是无法安全地选择与已安装模块冲突的插件名称。


作为一个完全独立的问题,您的Plugin类完全忽略其path参数是很奇怪的。