Python language basics: understanding exception handling
Introduction to exceptions
Ideally, an application will always work exactly as expected. In reality, things can go wrong for several reasons. A design flaw or issue with the system running the program could cause an application to crash. Or a malicious user may attempt to exploit the application, providing it with invalid input to attempt to trigger an injection vulnerability.
The potential for issues within a Python application makes exception handling a vital part of Python programming. Done properly, exception handling can enable an application to recover from an issue and continue operation or at least fail cleanly while providing useful information to the user about what went wrong.
Python has several built-in exceptions. These address universal errors that can cause a program to fail or a calculation to be incorrect. Examples of these include attempts to divide by zero or a failure when trying to process user input.
While these built-in exceptions cover many cases, a Python developer may want the ability to create custom exceptions as well. For example, in an authentication function, it may be useful to be able to throw an exception if a user provides the wrong password for an account.
Python allows exceptions to be thrown using the raise command. This command is a function, so it is possible to use commands like raise Exception(‘Error Text’) to raise an exception that has developer-provided text. It is also possible to use shorthand such as raise NameError to raise a particular exception without providing additional information.
Python’s support for custom exceptions can be a valuable tool for developers. It enables code to be more flexible and user-friendly because custom errors can be raised to describe and handle particular issues.
Exception management in Python
There are two ways in which a Python application can handle an exception. The simplest is to not handle it at all. If a function does not “catch” any exceptions thrown by its code, it returns and responsibility is passed to the calling function. Eventually, this can cause a program to crash if no function properly manages the exception.
The other option is for a Python function to attempt to handle the issue itself. Python makes this possible using the try-except-finally structure.
Try, except, else and finally
Many programming languages have structures for developers to handle errors and exceptions. The Python programming language has four keywords for exception handling:
- Try: The try block contains the code that can potentially throw an exception
- Except: An except block is designed to handle a particular exception. A block can handle one or more specific exceptions (place multiple exceptions in a tuple) or generally handle any exception thrown within the try block (by not specifying a particular exception)
- Else: The else block is executed if a try block does not throw an exception. This is useful if there is functionality that should be executed only if nothing goes wrong since code after the exception handling structure will be run regardless
- Finally: Code in a finally block is run whether or not an exception is thrown. It is executed after all except and else blocks have been executed
Python’s exception handling structure is very flexible. It’s possible to define multiple different except blocks to handle different specific errors, and the else and finally blocks are completely optional. This makes it possible to tune the exception management code to address the precise issue raised in the try block.
Secure exception management in Python
Python exception handling is designed to allow a developer to manage errors within their application. However, there are ways in which exception handling can go wrong, including:
- Not handling exceptions: Except blocks do not need to contain error-handling code. An empty except block may seem like a good way to allow the code to continue running in the face of an issue, but it leaves the issue itself unfixed.
- Using general exception handling: A Python except block can act as a catchall for errors by not specifying which exceptions it handles. Doing this creates the possibility that an overlooked exception will not be handled. For example, a try block may have both a ValueError and an IOError but only handle the ValueError
- Handling the wrong exception: A try block may have multiple types of exceptions, which may need to be handled differently. If a try block does not handle all potential exceptions properly, the program may crash or continue running in an invalid state
When implementing exception handling in Python, it is best practice to specify the exception handled by a particular except block as specifically as possible. This decreases the probability that an oversight will result in vulnerable code.