【问题标题】:Handling exceptions raised from imported modules using importlib.import_module使用 importlib.import_module 处理导入模块引发的异常
【发布时间】:2020-02-09 23:07:37
【问题描述】:

我有一个简单的插件系统,简化形式如下。想法是插件将实现一个抽象类,并可以引发异常以发出拆卸信号。

# my_plugin.py
import my_app


class MyPlugin(my_app.MyPluginBase):

    def start(self):
        raise my_app.MyLibException()
# my_app.py
import abc
import importlib


class MyLibException(Exception):
    pass


class MyPluginBase(abc.ABC):

    @abc.abstractmethod
    def start(self):
        pass


def main():
    module = importlib.import_module('my_plugin')
    klass = getattr(module, 'MyPlugin')
    try:
        app = klass()
        app.start()
    except MyLibException as e:
        print('ok')
        print(e.__class__)
    except Exception as e:
        print('not ok')
        print(e.__class__)


if __name__ == '__main__':
    main()

运行上述结果:

not ok
<class 'my_app.MyLibException'>

在这种情况下处理异常的正确方法是什么?我想在这里捕获引发的异常except MyLibException as e: 而不是except Exception as e:

【问题讨论】:

  • 我不确定这是否正确,但是如果在my_app.py 中定义MyLibException 后添加from my_app import MyLibException,则会处理异常并打印“ok”。
  • 这看起来像是一些反模式,在同一个文件中导入一个类,以前没见过。您可能见过类似的参考实现吗?谢谢
  • 我同意它看起来像一个反模式,我还没有找到任何这样的参考实现。我认为处理它的最佳方法是完全避免循环依赖并在单独的文件中定义MyLibException
  • 同意!将异常放在单独的模块/文件中将使其工作,不幸的是,这不是我的选择,因为这里的想法是让my_app.py 通过plugins 扩展单个文件模块。但是请注意,如果 my_app.py 开始直接导入 my_plugin.py,一切都会按预期工作,只是使用 importlib 时不会。
  • 你确定直接导入my_plugin 可以吗?当我尝试时,我仍然遇到同样的问题。如果您真的坚持将所有内容保存在一个文件中,希望我的回答能提供足够的信息。但是,如果您担心反模式,您应该注意循环导入也不是一个好习惯。

标签: python python-3.x plugins python-import python-importlib


【解决方案1】:

问题的根本原因是MyLibExceptionmy_app.py中被识别为__main__的属性,但是在my_plugin.py中导入时却是my_app的属性。

最干净的解决方案是将 MyLibException 与其他模块分开,以避免完全循环依赖,但由于您在 cmets 中提到这不是一个选项,我能想到的唯一方法是在其中导入异常同一个文件。这显然不是一个好的做法,但对于一般的循环导入也可以这样说。

实现这一点的几种方法是:

  1. 通过将import my_app(或import __main__ as my_app)添加到 my_app.py 并使用 except my_app.MyLibException 捕获异常

  2. 通过使用 importlib 就像您在导入 my_plugin 时所做的那样:my_app = importlib.import_module('my_app') 并以与 1 中相同的方式捕获。

  3. from __main__ import MyLibException 在定义 MyLibException 之后,但是这违反了 PEP8 声明:

    导入总是放在文件的顶部,就在任何模块 cmets 和文档字符串之后,模块全局变量和常量之前。

有关这可能不是一个好主意的更多建议和原因,另请参阅:

【讨论】:

  • 感谢 Jonathan 提供的选项。使用选项#1,我们将在不同的范围内得到 2 个例外。同样在 my_app 中,我必须处理 my_lib.MyLibException 和 MyLibException 这两个异常,因为 my_app 中的代码也可以抛出异常。从长远来看,拥有单独的模块对我来说是最好的方法,我正在与社区讨论。 FWIW,我正在为github.com/abhinavsingh/proxy.py 解决这个问题:)谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-29
相关资源
最近更新 更多