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:.
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:
By adding import my_app (or import __main__ as my_app) to
my_app.py and catching the exception using except my_app.MyLibException
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.
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:
import __main__?If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With