Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling exceptions raised from imported modules using importlib.import_module

I have a simple plugin system, simplified form below. Idea is that plugins will implement an abstract class and can raise an exception to signal teardown.

# 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()

Running above results in:

not ok
<class 'my_app.MyLibException'>

What's the correct way of handling exceptions for such a scenario? I would like to catch the raised exception here except MyLibException as e: rather than except Exception as e:.

like image 628
Abhinav Singh Avatar asked May 25 '26 12:05

Abhinav Singh


1 Answers

The root cause of the problem is that MyLibException is recognised as an attribute of __main__ in my_app.py, but when importing it in my_plugin.py it is an attribute of my_app.

The cleanest solution would be to separate MyLibException from the other modules to avoid circular dependencies altogether, but since you mentioned in the comments that this is not an option, the only way I can think of is by importing the exception within the same file. This is obviously not a good practice, but the same can be said about circular imports in general.

Several ways to implement this are:

  1. By adding import my_app (or import __main__ as my_app) to my_app.py and catching the exception using except my_app.MyLibException

  2. By using importlib like you did for importing my_plugin: my_app = importlib.import_module('my_app') and catching the same way as in 1.

  3. from __main__ import MyLibException after defining MyLibException, however this is a violation of PEP8 which states:

    Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants.

For more suggestions and reasons why this might not be a good idea, see also:

  • In Python, how can I access the namespace of the main module from an imported module?
  • Is it good practice to use import __main__?
like image 105
Jonathan Feenstra Avatar answered May 27 '26 04:05

Jonathan Feenstra