Why Is "Except: Pass" a Bad Programming Practice

Why is except: pass a bad programming practice?

As you correctly guessed, there are two sides to it: Catching any error by specifying no exception type after except, and simply passing it without taking any action.

My explanation is “a bit” longer—so tl;dr it breaks down to this:

  1. Don’t catch any error. Always specify which exceptions you are prepared to recover from and only catch those.
  2. Try to avoid passing in except blocks. Unless explicitly desired, this is usually not a good sign.

But let’s go into detail:

Don’t catch any error

When using a try block, you usually do this because you know that there is a chance of an exception being thrown. As such, you also already have an approximate idea of what can break and what exception can be thrown. In such cases, you catch an exception because you can positively recover from it. That means that you are prepared for the exception and have some alternative plan which you will follow in case of that exception.

For example, when you ask for the user to input a number, you can convert the input using int() which might raise a ValueError. You can easily recover that by simply asking the user to try it again, so catching the ValueError and prompting the user again would be an appropriate plan. A different example would be if you want to read some configuration from a file, and that file happens to not exist. Because it is a configuration file, you might have some default configuration as a fallback, so the file is not exactly necessary. So catching a FileNotFoundError and simply applying the default configuration would be a good plan here. Now in both these cases, we have a very specific exception we expect and have an equally specific plan to recover from it. As such, in each case, we explicitly only except that certain exception.

However, if we were to catch everything, then—in addition to those exceptions we are prepared to recover from—there is also a chance that we get exceptions that we didn’t expect, and which we indeed cannot recover from; or shouldn’t recover from.

Let’s take the configuration file example from above. In case of a missing file, we just applied our default configuration and might decide at a later point to automatically save the configuration (so next time, the file exists). Now imagine we get a IsADirectoryError, or a PermissionError instead. In such cases, we probably do not want to continue; we could still apply our default configuration, but we later won’t be able to save the file. And it’s likely that the user meant to have a custom configuration too, so using the default values is likely not desired. So we would want to tell the user about it immediately, and probably abort the program execution too. But that’s not something we want to do somewhere deep within some small code part; this is something of application-level importance, so it should be handled at the top—so let the exception bubble up.

Another simple example is also mentioned in the Python 2 idioms document. Here, a simple typo exists in the code which causes it to break. Because we are catching every exception, we also catch NameErrors and SyntaxErrors. Both are mistakes that happen to us all while programming and both are mistakes we absolutely don’t want to include when shipping the code. But because we also caught those, we won’t even know that they occurred there and lose any help to debug it correctly.

But there are also more dangerous exceptions which we are unlikely prepared for. For example, SystemError is usually something that happens rarely and which we cannot really plan for; it means there is something more complicated going on, something that likely prevents us from continuing the current task.

In any case, it’s very unlikely that you are prepared for everything in a small-scale part of the code, so that’s really where you should only catch those exceptions you are prepared for. Some people suggest to at least catch Exception as it won’t include things like SystemExit and KeyboardInterrupt which by design are to terminate your application, but I would argue that this is still far too unspecific. There is only one place where I personally accept catching Exception or just any exception, and that is in a single global application-level exception handler which has the single purpose to log any exception we were not prepared for. That way, we can still retain as much information about unexpected exceptions, which we then can use to extend our code to handle those explicitly (if we can recover from them) or—in case of a bug—to create test cases to make sure it won’t happen again. But of course, that only works if we only ever caught those exceptions we were already expecting, so the ones we didn’t expect will naturally bubble up.

Try to avoid passing in except blocks

When explicitly catching a small selection of specific exceptions, there are many situations in which we will be fine by simply doing nothing. In such cases, just having except SomeSpecificException: pass is just fine. Most of the time though, this is not the case as we likely need some code related to the recovery process (as mentioned above). This can be for example something that retries the action again, or to set up a default value instead.

If that’s not the case though, for example, because our code is already structured to repeat until it succeeds, then just passing is good enough. Taking our example from above, we might want to ask the user to enter a number. Because we know that users like to not do what we ask them for, we might just put it into a loop in the first place, so it could look like this:

def askForNumber ():
while True:
try:
return int(input('Please enter a number: '))
except ValueError:
pass

Because we keep trying until no exception is thrown, we don’t need to do anything special in the except block, so this is fine. But of course, one might argue that we at least want to show the user some error message to tell him why he has to repeat the input.

In many other cases though, just passing in an except is a sign that we weren’t really prepared for the exception we are catching. Unless those exceptions are simple (like ValueError or TypeError), and the reason why we can pass is obvious, try to avoid just passing. If there’s really nothing to do (and you are absolutely sure about it), then consider adding a comment why that’s the case; otherwise, expand the except block to actually include some recovery code.

except: pass

The worst offender though is the combination of both. This means that we are willingly catching any error although we are absolutely not prepared for it and we also don’t do anything about it. You at least want to log the error and also likely reraise it to still terminate the application (it’s unlikely you can continue like normal after a MemoryError). Just passing though will not only keep the application somewhat alive (depending on where you catch of course), but also throw away all the information, making it impossible to discover the error—which is especially true if you are not the one discovering it.


So the bottom line is: Catch only exceptions you really expect and are prepared to recover from; all others are likely either mistakes you should fix or something you are not prepared for anyway. Passing specific exceptions are fine if you really don’t need to do something about them. In all other cases, it’s just a sign of presumption and being lazy. And you definitely want to fix that.

Is it a good practice to use try-except-else in Python?

"I do not know if it is out of ignorance, but I do not like that
kind of programming, as it is using exceptions to perform flow control."

In the Python world, using exceptions for flow control is common and normal.

Even the Python core developers use exceptions for flow-control and that style is heavily baked into the language (i.e. the iterator protocol uses StopIteration to signal loop termination).

In addition, the try-except-style is used to prevent the race-conditions inherent in some of the "look-before-you-leap" constructs. For example, testing os.path.exists results in information that may be out-of-date by the time you use it. Likewise, Queue.full returns information that may be stale. The try-except-else style will produce more reliable code in these cases.

"It my understanding that exceptions are not errors, they should only
be used for exceptional conditions"

In some other languages, that rule reflects their cultural norms as reflected in their libraries. The "rule" is also based in-part on performance considerations for those languages.

The Python cultural norm is somewhat different. In many cases, you must use exceptions for control-flow. Also, the use of exceptions in Python does not slow the surrounding code and calling code as it does in some compiled languages (i.e. CPython already implements code for exception checking at every step, regardless of whether you actually use exceptions or not).

In other words, your understanding that "exceptions are for the exceptional" is a rule that makes sense in some other languages, but not for Python.

"However, if it is included in the language itself, there must be a
good reason for it, isn't it?"

Besides helping to avoid race-conditions, exceptions are also very useful for pulling error-handling outside loops. This is a necessary optimization in interpreted languages which do not tend to have automatic loop invariant code motion.

Also, exceptions can simplify code quite a bit in common situations where the ability to handle an issue is far removed from where the issue arose. For example, it is common to have top level user-interface code calling code for business logic which in turn calls low-level routines. Situations arising in the low-level routines (such as duplicate records for unique keys in database accesses) can only be handled in top-level code (such as asking the user for a new key that doesn't conflict with existing keys). The use of exceptions for this kind of control-flow allows the mid-level routines to completely ignore the issue and be nicely decoupled from that aspect of flow-control.

There is a nice blog post on the indispensibility of exceptions here.

Also, see this Stack Overflow answer: Are exceptions really for exceptional errors?

"What is the reason for the try-except-else to exist?"

The else-clause itself is interesting. It runs when there is no exception but before the finally-clause. That is its primary purpose.

Without the else-clause, the only option to run additional code before finalization would be the clumsy practice of adding the code to the try-clause. That is clumsy because it risks
raising exceptions in code that wasn't intended to be protected by the try-block.

The use-case of running additional unprotected code prior to finalization doesn't arise very often. So, don't expect to see many examples in published code. It is somewhat rare.

Another use-case for the else-clause is to perform actions that must occur when no exception occurs and that do not occur when exceptions are handled. For example:

recip = float('Inf')
try:
recip = 1 / f(x)
except ZeroDivisionError:
logging.info('Infinite result')
else:
logging.info('Finite result')

Another example occurs in unittest runners:

try:
tests_run += 1
run_testcase(case)
except Exception:
tests_failed += 1
logging.exception('Failing test case: %r', case)
print('F', end='')
else:
logging.info('Successful test case: %r', case)
print('.', end='')

Lastly, the most common use of an else-clause in a try-block is for a bit of beautification (aligning the exceptional outcomes and non-exceptional outcomes at the same level of indentation). This use is always optional and isn't strictly necessary.

How to properly ignore exceptions

try:
doSomething()
except Exception:
pass

or

try:
doSomething()
except:
pass

The difference is that the second one will also catch KeyboardInterrupt, SystemExit and stuff like that, which are derived directly from BaseException, not Exception.

See documentation for details:

  • try statement
  • exceptions

However, it is generally bad practice to catch every error - see Why is "except: pass" a bad programming practice?

Using except to mean and if that doesn't work

Maybe it'd be better to put it into a dedicated function ?

def func(source):
try:
return source.geturl()
except Exception:
try:
return pathlib.Path(os.path.abspath(source.name)).as_uri()
except Exception:
pass

Notes on swallowing exceptions

Ignoring any kind of exception is not the best pattern : it'd be better to know what call can raise what kind of exception.

Even if except ...: pass is bad, that's already what you were doing in your example, and here it's clear that we try another way if it fails.

The bad programming practice is basing the error handling on the ability to catch any kind of error. In itself, except: pass is better than except: <lots_of_things_that_wont_raise_again>.

However, the first answer in the post you referred to says something very sensible : pass semantically indicates that you won't do ANYTHING with the exception, and as you do not store in a variable, it also shows that you'll never be able to access it again (well you could, but still..). As /u/Aprillion/, you'll want to at least log these errors, because otherwise, you may be swallowing very useful debugging information, which could make your life (or others) much more difficult at some point.

For example, what happens if there's a bug hidden in geturl, that makes it raises exception in normal cases ? It may stay hidden for long, because this exception will be swallowed and never shown anywhere. Good luck to the debugger to find it !

Ways to improve this

  • replace except Exception with the exceptions that could be raised
  • check before an attempt if it is going to fail, so that you don't have to catch an exception

Additionally, you probably want to use a logger to save/show these exceptions somewhere, at least in DEBUG mode.

Clearly with some spelunking I could figure out which specific errors to catch with a reasonable degree of confidence. But at is explained in this question (Python: How can I know which exceptions might be thrown from a method call) you can't ever be quite certain.

Why would that change anything ? If geturl returns specific exception types, you'd only catch these, and you'd want other unexpected errors to bubble up to the interpreter.

The big problem with the givn approach is that if geturl is undefined, or not callable, or take more arguments, this error would not make the interpreter crash, even though it is a clear programming error. That's because except: or except Exception: will catch a lof of Python errors, should it be AttributeError, NameError, ImportError or even SyntaxError.

To conclude, I really think you'd prefer to replace Exception with the list of exceptions you can catch (and at least send the exceptions to a logger). Note that you can write it as such :

   try:
return source.geturl()
except (ExceptionA, ExceptionB, ExceptionC):
pass

Is it bad practice to have a function that doesn't return if it throws an exception?

The purpose of raising an exception is to provide an alternate exit point in the event where a valid return value can't be found. You are using exceptions exactly as they are intended.

However, I would probably check if exhale_dir is non-positive first, which would save you from performing a calculation with an invalid value.

if exhale_dur <= 0:
raise error.ConfigurationError
elif (inh_dur + breath_hold_dur + exhale_dur) > single_breath_dur):
raise error.ConfigurationError

# You can also omit the else, since the only way to reach this point
# is to *not* have raised an exception. This is a matter of style, though.
return exhale_dur

Is is bad practice to write a method that does nothing except throw an exception?

There is nothing wrong with that.

The .NET framework does this too: for example, CancellationToken has a method ThrowIfCancellationRequested which does nothing but throw or not throw depending on some condition.

Another example: Dispatcher's VerifyAccess method, which checks if the caller is on the same thread as the control is supposed to be accessed on, and throws if not.

usage of keyword pass in python

One use is to define a "stub" class or method that has a name but doesn't do anything. Maybe the most common case of this is to define a custom Exception class:

class MyCustomException(Exception):
pass

Now you can raise and catch exceptions of this type. The class exists only to be raised and caught, and doesn't need any behavior associated with it.

In similar fashion, APIs may include "do-nothing" functions that are meant to be overridden in subclasses:

class SomeComplexAPI(object):
# This method may be overridden in subclasses, but the base-class implementation is a no-op
def someAPIHook(self):
pass

If you didn't define the method at all, SomeComplexAPI().someAPIHook() would raise an exception because the method doesn't exist. Defining it with pass makes sure you can safely call it even if it doesn't do anything.

Is creating a variable in try/except block considered a bad practice?

try:
my_index = li.index(None)
except ValueError:
my_index = len(li) - 1

print my_index

Possible problem - different exception type will result in not creating my_index and crashing the script on print.

Err, no. If any other exception is raised, this entire block of code will be aborted as the exception bubbles up to the caller. So if any other exception type besides ValueError is raised, print will never be executed. Which is a pretty sane behaviour: you handle the errors you expect and can handle, and let other errors cause the entire block to abort, possibly the entire script. This is the sanest and most concise solution for what you want.

Are nested try/except blocks in Python a good programming practice?

Your first example is perfectly fine. Even the official Python documentation recommends this style known as EAFP.

Personally, I prefer to avoid nesting when it's not necessary:

def __getattribute__(self, item):
try:
return object.__getattribute__(item)
except AttributeError:
pass # Fallback to dict
try:
return self.dict[item]
except KeyError:
raise AttributeError("The object doesn't have such attribute") from None

PS. has_key() has been deprecated for a long time in Python 2. Use item in self.dict instead.



Related Topics



Leave a reply



Submit