Обработка исключений, вызванных импортированными модулями, с помощью importlib.import_module - PullRequest
1 голос
/ 12 октября 2019

У меня есть простая система плагинов, упрощенная форма ниже. Идея состоит в том, что плагины будут реализовывать абстрактный класс и могут вызывать исключение для разрыва сигнала.

# 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:.

1 Ответ

1 голос
/ 14 октября 2019

Основной причиной проблемы является то, что MyLibException распознается как атрибут __main__ в my_app.py, но при импорте его в my_plugin.py это атрибут my_app.

Самым чистым решением было бы отделить MyLibException от других модулей, чтобы вообще избежать циклических зависимостей, но, поскольку вы упомянули в комментариях, что это не вариант, я могу думать только об импортеисключение в том же файле. Очевидно, что это не очень хорошая практика, но то же самое можно сказать и о круговом импорте в целом.

Несколько способов реализовать это:

  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 , которое гласит:

    Импорт всегда помещается вверху файла, только после любых комментариев и строк документации, а также перед глобальными переменными и константами модуля.

Дополнительные предложения и причины, по которым это может быть не очень хорошей идеей, см. также:

...