92

I have a list of functions that may fail and, if one fails, I don't want the script to stop, but to continue with next function.

I am executing it with something like this :

list_of_functions = [f_a, f_b, f_c]
for current_function in list_of_functions:
    try:
        current_function()
    except Exception:
        print(traceback.format_exc())

It's working fine, but it is not PEP8 compliant:

When catching exceptions, mention specific exceptions whenever possible instead of using a bare except: clause.

For example, use:

try:
    import platform_specific_module
except ImportError:
    platform_specific_module = None

A bare except: clause will catch SystemExit and KeyboardInterrupt exceptions, making it harder to interrupt a program with Control-C, and can disguise other problems. If you want to catch all exceptions that signal program errors, use except Exception: (bare except is equivalent to except BaseException: ).

A good rule of thumb is to limit use of bare 'except' clauses to two cases:

If the exception handler will be printing out or logging the traceback; at least the user will be aware that an error has occurred.

If the code needs to do some cleanup work, but then lets the exception propagate upwards with raise . try...finally can be a better way to handle this case.

How can I do this the good way?

David Buck
  • 3,752
  • 35
  • 31
  • 35
Blusky
  • 3,470
  • 1
  • 19
  • 35

8 Answers8

79

The PEP8 guide you quote suggests that it is okay to use a bare exception in your case provided you are logging the errors. I would think that you should cover as many exceptions as you can/know how to deal with and then log the rest and pass, e.g.

import logging

list_of_functions = [f_a,f_b,f_c]
for current_function in list_of_functions:
    try:
        current_function()
    except KnownException:
        raise
    except Exception as e:
        logging.exception(e)
Christophe Roussy
  • 16,299
  • 4
  • 85
  • 85
Ed Smith
  • 12,716
  • 2
  • 43
  • 55
  • 1
    `except KeyboardInterrupt: raise` is strictly unnecessary here. `KeyboardInterrupt` will never be caught by `except Exception` because `KeyboardInterrupt` derives from `BaseException`, not `Exception`. – roippi May 25 '15 at 20:35
  • 1
    Just wanted an example of a known exception, didn't realise `KeyboardInterrupt` is a bad example. I've changed it to some generic `KnownException`... – Ed Smith May 25 '15 at 20:38
  • 1
    A bare except is more suitable here. `logging.exception()` will automatically record the error and traceback. – lovetl2002 Oct 16 '17 at 12:44
  • 4
    Hey, I am doing this in a couple places and yet, pylint throws too broad an exception.. – ScipioAfricanus Oct 30 '19 at 20:47
47

Use this to cheat PEP8:

try:
    """code"""

except (Exception,): 
    pass
Allex Radu
  • 1,257
  • 13
  • 24
  • 5
    cleanest solution... one extra char only, nothing unnecessary or non-truthinessedly added. I think this check is crazy... even if you predict all possible exceptions, you should always have the catchall at the end for things not predicted... it is pervasively recommend. – gunslingor Jun 29 '21 at 12:43
  • 1
    What does this do? – Yair V. Oct 14 '21 at 07:36
  • 3
    @Yair It provides a catch-all exception for all the possible errors that could occur, without any complaints from your IDE. – Allex Radu Oct 15 '21 at 22:59
  • 2
    How to log the exception in this case? `except (Exception as e, ):` seems not allowd in braces – sjd Jan 25 '22 at 10:45
  • 5
    except (Exception,) as e: – Allex Radu Jan 29 '22 at 08:39
  • `flake8` reports this as an E722, "do not use base `except`". – bers Feb 25 '22 at 09:33
18

I think in some rare cases catching general exception is just justified and there is a way to trick PEP8 inspection:

list_of_functions = [f_a,f_b,f_c]
for current_function in list_of_functions:
try:
    current_function()
except (ValueError, Exception):
    print(traceback.format_exc())

You can replace ValueError by any other. It works for me (at least in PyCharm).

10

You can just put a comment like except Exception as error: # pylint: disable=broad-except that's worked for me actually. I hope it could be work for you.

Hushen
  • 427
  • 1
  • 4
  • 15
  • 3
    Actually, this comment is a concise way of saying: "I'm aware that broad exceptions should be avoided, but in this case I want it and I know what I'm doing." --- You can set your workflow to search for such directive, if you think people abuse them. – eternal_student Apr 08 '21 at 18:01
  • Should be the accepted answer, so clean ! – CBrunain Mar 15 '23 at 16:05
3

From issue PY-9715 on yourtrack.jetbrains.com:

"Too broad exception clauses" inspection

From pep-0348:

BaseException

The superclass that all exceptions must inherit from. It's name was chosen to reflect that it is at the base of the exception hierarchy while being an exception itself. "Raisable" was considered as a name, it was passed on because its name did not properly reflect the fact that it is an exception itself.

Direct inheritance of BaseException is not expected, and will be discouraged for the general case. Most user-defined exceptions should inherit from Exception instead. This allows catching Exception to continue to work in the common case of catching all exceptions that should be caught. Direct inheritance of BaseException should only be done in cases where an entirely new category of exception is desired.

But, for cases where all exceptions should be caught blindly, except BaseException will work.

kalehmann
  • 4,821
  • 6
  • 26
  • 36
xgqfrms
  • 10,077
  • 1
  • 69
  • 68
1

You can avoid the error if you then re-raise the Exception. This way you are able to do damage control and not endanger loosing track of its occurance.

Javo
  • 435
  • 1
  • 5
  • 16
0

Do you perhaps mean that each function can raise different exceptions? When you name the exception type in the except clause it can be any name that refers to an exception, not just the class name.

eg.

def raise_value_error():
    raise ValueError

def raise_type_error():
    raise TypeError

def raise_index_error():
    doesnt_exist

func_and_exceptions = [(raise_value_error, ValueError), (raise_type_error, TypeError), 
    (raise_index_error, IndexError)]

for function, possible_exception in func_and_exceptions:
   try:
       function()
   except possible_exception as e:
       print("caught", repr(e), "when calling", function.__name__)

prints:

caught ValueError() when calling raise_value_error
caught TypeError() when calling raise_type_error
Traceback (most recent call last):
  File "run.py", line 14, in <module>
    function()
  File "run.py", line 8, in raise_index_error
    doesnt_exist
NameError: name 'doesnt_exist' is not defined

Of course that leaves you with not knowing what to do when each exception occurs. But since you just want to ignore it and carry on then that's not a problem.

Dunes
  • 37,291
  • 7
  • 81
  • 97
0

First, generate the pylintrc using the below command

pylint --generate-rcfile > .pylintrc

For reference: https://learn.microsoft.com/en-us/visualstudio/python/linting-python-code?view=vs-2022

Search for disable (uncomment if needed) in the generate pylintrc file and add the below exception.

broad-except

Rerun the pylint command and see the magic

Saurabh
  • 975
  • 2
  • 12
  • 27