Suppose I have a project with a folder structure like so.
/project
__init__.py
main.py
/__helpers
__init__.py
helpers.py
...
The module helpers.py
defines some exception and contains some method that raises that exception.
# /project/__helpers/helpers.py
class HelperException(Exception):
pass
def some_function_that_raises():
raise HelperException
On the other hand my main.py
module defines its own exceptions and imports methods that may raise an exception from helpers.py
.
# /projects/main.py
from project.__helpers.helpers import some_function_that_raises
class MainException(Exception):
pass
Now, I do not want users to have to do from project.__helpers.helpers import HelperException
if they want to catch that exception. It would make more sense to be able to import the exception from the public module that is raising it.
But I cannot just move HelperException
to main.py
, which would create a circular import.
What would be the best way to allow users to import all exceptions from main.py
while those being raised in /__helpers
?
Why is it best practice to have multiple except statements with each type of error labeled correctly? Ensure the error is caught so the program will terminate In order to know what type of error was thrown and the.
In Python, exceptions can be handled using a try statement. The critical operation which can raise an exception is placed inside the try clause. The code that handles the exceptions is written in the except clause. We can thus choose what operations to perform once we have caught the exception.
When a Python code throws an exception, it has two options: handle the exception immediately or stop and quit.
Some of the best practices for exception handling in python are given below: Exceptions are better than returning error status codes. We have to handle exceptions in Python as the whole language core and standard libraries throw exceptions. Elegantly handled exceptions are any day preferable to error codes and trace backs.
But yes, exceptions can and are used for flow control in Python. It might be a bad practice to do so without abstracting over the mechanics (like is done for the iterator protocol by using next method and for statement), though.
Python also allows you to create your own exceptions by deriving classes from the standard built-in exceptions. Here is an example related to RuntimeError. Here, a class is created that is subclassed from RuntimeError. This is useful when you need to display more specific information when an exception is caught.
To differentiate application-level exceptions from other python exceptions, we create a specialized class, which inherits from python’s Exception class. Throw it when there is an application-level error, and catch it in the main code.
Here is the solution I came up with.
The idea is basically to put all the exceptions in one file from which they can be imported and then import them all in main.py
. To make everything clean and explicit, we finally define the __all__
attribute of the module.
Here is the new file structure
/project
__init__.py
main.py
/__helpers
__init__.py
exceptions.py
helpers.py
...
Here is the exceptions.py
file.
# /project/__helpers/exceptions.py
class MainException(Exception):
pass
# Note that this also allows us to inherit between exceptions
class HelperException(MainException):
pass
Then we can import exceptions from that file without risk of circular dependency.
And finally we define __all__
in main.py
to make it explicit that the exceptions are to be imported.
# /projects/main.py
from project.__helpers.helpers import some_function_that_raises
from project.__helpers.exceptions import MainException, HelperException
__all__ = ['MainException', 'HelperException', ...]
Just a reminder the __all__
attribute defines what will be imported if one was to do from project import *
. So this both extends the desired behavior to import star and makes it explicit that we want the exceptions to be imported from that file.
Also note that some IDEs will even treat 'HelperException'
being in __all__
as a reference to HelperException
and will not bother you from having an unused import. This is what leads me to think this is the correct approach.
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