Home > Net >  Python generic exception bad practice concept
Python generic exception bad practice concept

Time:09-25

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 ZeroDivisionErrors, 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

  • Related