Raising Exceptions
So far, the examples in this chapter have reacted to exceptions. Something happens and the application provides error-handling support for that event. However, situations arise for which you may not know how to handle an error event during the application design process. Perhaps you can’t even handle the error at a particular level and need to pass it up to some other level to handle. In short, in some situations, your application must generate an excep- tion. This act is called raising (or sometimes throwing) the exception. The fol- lowing sections describe common scenarios in which you raise exceptions in specific ways.
Raising exceptions during exceptional conditions
The example in this section demonstrates how you raise a simple exception — that it doesn’t require anything special. The following steps simply create the exception and then handle it immediately. This example also appears with the downloadable source code as RaiseException1.py.
- Open a Python File window.
You see an editor in which you can type the example code.
2y.peT the following code into the window — pressing Enter after each line:
You wouldn’t ever actually create code that looks like this, but it shows you how raising an exception works at its most basic level. In this case, the raise call appears within a try . . . except block. A basic raise call simply provides the name of the exception to raise (or throw). You can also provide arguments as part of the output to provide addi- tional information.
Notice that this try . . . except block lacks an else clause because there is nothing to do after the call. Although you rarely use a try . . . except block in this manner, you can. You may encounter situations like this one sometimes and need to remember that adding the else clause is purely optional. On the other hand, you must add at least one except clause.
- Choose Run➪Run Module.
You see a Python Shell window open. The application displays the expected exception text, as shown in Figure 9-12.
Figure 9-12:
Raising an excep tion only requires a call to raise.
Passing error information to the caller
Python provides exceptionally flexible error handling in that you can pass information to the caller (the code that is calling your code) no matter which exception you use. Of course, the caller may not know that the information is available, which leads to a lot of discussion on the topic. If you’re working with someone else’s code and don’t know whether additional information is
available, you can always use the technique described in the “Obtaining a list of exception arguments” sidebar earlier in this chapter to find it.
You may have wondered whether you could provide better information when working with a ValueError exception than with an exception provided natively by Python. The following steps show that you can modify the output so that it does include helpful information. This example also appears with the downloadable source code as RaiseException2.py.
- Open a Python File
You see an editor in which you can type the example code.
2y.peT the following code into the window — pressing Enter after each line:
The ValueError exception normally doesn’t provide an attribute named strerror (a common name for string error), but you can add it simply by assigning a value to it as shown. When the example raises the exception, the except clause handles it as usual but obtains access to the attributes using e. You can then access the e.strerror member to obtain the added information.
- Choose Run➪Run Module.
You see a Python Shell window open. The application displays an expanded ValueError exception, as shown in Figure 9-13.
Figure 9-13: It’s possible to add error informa tion to any exception.
Creating and Using Custom Exceptions
Python provides a wealth of standard exceptions that you should use when- ever possible. These exceptions are incredibly flexible, and you can even modify them as needed (within reason) to meet specific needs. For example, the “Passing error information to the caller” section of this chapter demon- strates how to modify a ValueError exception to allow for additional data. However, sometimes you simply must create a custom exception because none of the standard exceptions will work. Perhaps the exception name just doesn’t tell the viewer the purpose that the exception serves. You may need a custom exception for specialized database work or when working with a service.
The example in this section is going to seem a little complicated for now because you haven’t worked with classes before. Chapter 14 introduces you to classes and helps you understand how they work. If you want to skip this sec- tion until after you read Chapter 14, you can do so without any problem.
The example in this section shows a quick method for creating your own exceptions. To perform this task, you must create a class that uses an exist- ing exception as a starting point. To make things a little easier, this example creates an exception that builds upon the functionality provided by the ValueError exception. The advantage of using this approach rather than the one shown in the “Passing error information to the caller” section, the preceding section in this chapter, is that this approach tells anyone who follows you precisely what the addition to the ValueError exception is; additionally, it makes the modified exception easier to use. This example also appears with the downloadable source code as CustomException.py.
- Open a Python File
You see an editor in which you can type the example code.
2y.peT the following code into the window — pressing Enter after each line:
This example essentially replicates the functionality of the example in the “Passing error information to the caller” section of the chapter.
However, it places the same error in both strerror and args so that the developer has access to either (as would normally happen).
The code begins by creating the CustomValueError class that uses the ValueError exception class as a starting point. The init () func- tion provides the means for creating a new instance of that class. Think of the class as a blueprint and the instance as the building created from the blueprint.
Notice that the strerror attribute has the value assigned directly to it, but args receives it as an array. The args member normally contains an array of all the exception values, so this is standard procedure, even when args contains just one value as it does now.
The code for using the exception is considerably easier than modify- ing ValueError directly. All you do is call raise with the name of the exception and the arguments you want to pass, all on one line.
- Choose Run➪Run Module.
You see a Python Shell window open. The application displays the letter sequence, along with the letter number, as shown in Figure 9-14.
Figure 9-14:
Custom exceptions can make your code easier to
read.
Using the finally Clause
Normally you want to handle any exception that occurs in a way that doesn’t cause the application to crash. However, sometimes you can’t do anything to fix the problem, and the application is most definitely going to crash. At this point, your goal is to cause the application to crash gracefully, which means closing files so that the user doesn’t lose data and performing other tasks of that nature. Anything you can do to keep damage to data and the system to a minimum is an essential part of handling data for a crashing application.
The finally clause is part of the crashing-application strategy. You use this clause to perform any required last-minute tasks. Normally, the finally clause is quite short and uses only calls that are likely to succeed without further problem. It’s essential to close the files, log the user off, and per- form other required tasks, and then let the application crash before some- thing terrible happens (such as a total system failure). With this necessity
in mind, the following steps show a simple example of using the finally clause. This example also appears with the downloadable source code as ExceptionWithFinally.py.
- Open a Python File window.
You see an editor in which you can type the example code.
2y.peT the following code into the window — pressing Enter after each line:
In this example, the code raises a ValueError exception. The except clause executes as normal when this happens. The call to sys.exit() means that the application exits after the exception is handled. Perhaps the application can’t recover in this particular instance, but the applica- tion normally ends, which is why the final print() function call won’t ever execute.
The finally clause code always executes. It doesn’t matter whether the exception happens or not. The code you place in this block needs to be common code that you always want to execute. For example, when working with a file, you place the code to close the file into this block to ensure that the data isn’t damaged by remaining in memory rather than going to disk.
- Choose Run➪Run
You see a Python Shell window open. The application displays the except clause message and the finally clause message, as shown in Figure 9-15. The sys.exit() call prevents any other code from executing.
Figure 9-15:
Use the finally clause to ensure spe cific actions take place before the application
ends.
- Comment out the raise ValueError call by preceding it with two pound signs, like this:
Removing the exception will demonstrate how the finally clause actually works.
- Save the file to disk to ensure that Python sees the
- Choose Run➪Run
You see a Python Shell window open. The application displays a series of messages, including the finally clause message, as shown in Figure 9-16. This part of the example shows that the finally clause always executes, so you need to use it carefully.
Figure 9-16: It’s essential to remem ber that the finally
clause
always executes.