0

我正在尝试将我没有编写的 10 年历史事件侦听器从 Python 2.7 升级到 python 3.7。我遇到的基本问题是原始脚本导入其插件的方式。原始脚本背后的想法是,任何放入“plugins”文件夹的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 导入模块方式的变化(据我所知)的性质,其他留言板似乎都没有让我得到答案。
我尝试了几种不同的方法,迄今为止最好的方法是:
如何在给定完整路径的情况下导入模块?
我尝试了几种不同的方法,包括将完整路径添加到 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)

有没有人对我如何使用给定的文件夹结构实际导入这些模块有任何见解?提前致谢。

4

1 回答 1

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)

然后使用importor导入你的插件importlib.import_module。可能importlib.import_module,因为听起来插件加载器不会提前知道插件名称:

plugin = importlib.import_module(plugin_name)

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


作为一个完全独立的问题,你的Plugin班级完全忽略了它的path论点,这很奇怪。

于 2020-03-06T01:48:55.703 回答