I have the below script:
import jtp_aux as aux
def logger_path(path_logger):
return aux.init_logger(path_logger)
def generic_function(a, b):
return a/b
def main():
logger = logger_path(r'log_scratch.log')
try:
print(generic_function(2, 'l'))
except Exception as e:
logger.error(e, exc_info=True)
if __name__ == "__main__":
main()
I would need to catch any error thrown by the code and save into a file so I can analyse it later. However, I've been told that this generic exception catching is a bad practice and that the exceptions should be caught one by one, like this:
def main():
logger = logger_path(r'log_scratch.log')
try:
print(generic_function(2, 'l'))
except ValueError:
# Do something
except ZeroDivisionError:
# Do something
However, I'm struggling to understand why is that considered best practice if an unexpected exception could make the program fail without saving the error into a log. Any help with this will be hugely appreciated.
CodePudding user response:
If you need to catch any and all exceptions, except Exception
is exactly what you want and is fine. It doesn't make sense to make it any more fine grained than that, if you're doing the same handling for any and all exceptions anyway.
In general though, you should be as detailed as possible when catching exceptions to make sure you're catching expected exceptions and let unexpected exceptions bubble up. E.g.:
a = 1
b = 0
try:
c = a / B
except ...:
...
Here it makes a difference whether you catch only the expected ZeroDivisionError
or any error. By limiting yourself to expecting ZeroDivisionError
s, this code will correctly fail because of the NameError
due to the typo'd variable name. If you caught any and all exceptions, you could be debugging this code for quite a while before you find the typo.
CodePudding user response:
Your approach is fine since you really intend to catch all exception. Catching all exceptions is considered bad practice only if you actually only want to catch few. For instance, in the code below, if you expect get
to throw KeyError
if "Hello"
can't be fund, you should only catch KeyError
; if you catch everything, you might miss a different exception (e.g. ZeroDivisionError
):
try:
return myObj.get("Hello")
# bad: default value returned even though "Hello" might actually exist, but something else went wrong
except Exception as e:
return "DefaultValue"
# good: default value only returned on KeyError; all other exceptions are raised normally
except KeyError as e:
return "DefaultValue"
In your case, you could even re-raise the exception after logging it, making the program return to the regular exception handling:
try:
x = 1 / 0
except Exception as e:
print("Hello") # replace with logger.error(...) in your case
raise e # exits the except block and raises the exception again
Output:
Hello
Traceback (most recent call last):
File "pygame_textinput/tests.py", line 8, in <module>
raise e
File "pygame_textinput/tests.py", line 5, in <module>
x = 1 / 0
ZeroDivisionError: division by zero
If you do it like this, your code looks as though exceptions are raised normally from the outside, but you're also logging every exception you get. An example case where this could be important is when you're calling a function that is define somewhere else, and is expected to raise an exception in certain conditions. You want this exception to be raise as expected, not catch and ignore it. Read more here