0

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.

8
  • 2
    If you know the typical kind of error types you can and want to handle them differently you can do so without implementing your own pattern matching. If you handle everything the same then catching a plain Exception is fine. Commented Sep 24, 2021 at 9:53
  • 1
    its fine this way. you could even do raise e, causing no program behaviour change at all, apart from logging the exception. It's bad practice if you catch all errors even dough you only want to catch some. Say you're accessing a dict, but expect it to fail sometimes, so put it inside a try catch block. Then not checking for the specific exception could cause your program to ignore other, unrelated exceptions. Commented Sep 24, 2021 at 9:56
  • @Nearoo if I do raise e, will the program continue even if there is an error? Could you please help me with an example? Commented Sep 24, 2021 at 10:04
  • 1
    No, if you raise e again, then your program will stop. You're explicitly catching the exception to make your program not stop, even in the face of an error, right? Commented Sep 24, 2021 at 10:11
  • Well actually that would be the whole script so I might be better off by raising the exception then. Do I need to raise exception in each of the methods defined? Commented Sep 24, 2021 at 10:30

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

4 Comments

@OrenIshShalom …yes…?
@deceze thanks for the answer. Would it better to raise exceptions in the methods instead of doing a try except in the main?
@banana_99 Mu?!
I assume then this way is fine, thanks!
1

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 found, 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.

1 Comment

Great answer, thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.