In addition to responding to exceptions in other programs, you can raise exceptions in your own programs.
You can raise any of the existing exceptions, or you can create new ones of your own. Figure 9.1 lists all the built-in exceptions; you can also find their names by examining the directory of the __builtins__ object; all the exceptions contain the string “Error” or “Exit”.
You can create your own exceptions as subclasses of the built in Exception class. (Creating and working with classes is described in detail in Chap- ter 10.4.) To create a new exception called MyError, the following code can be used:
class MyError(Exception): pass
9.5. RAISING EXCEPTIONS 143 Exception StandardError Arithmetic Error FloatingPointError OverflowError ZeroDivisionError AssertionError AttributeError EnvironmentError IOError OSError EOFError ImportError KeyBoardInterrupt LookupError IndexError KeyError MemoryError NameError RuntimeError SyntaxError SystemExit TypeError ValueError
The pass statement serves as a placeholder in situations when Python ex- pects a statement, but you don’t need to do anything. No further details are needed to create a usable exception.
After creating an exception, you can use the raise statement to activate it. Suppose we are writing a function which will create an archive of files on a 100Mb zip disk. Before writing the archive, we will check to make sure that all the files will fit on the disk; if they will not, we’ll raise a SizeError exception. In this way, the calling program can decide on what action is appropriate. Here is the code to define the exception and create the archive: import shutil,os class SizeError(Exception): pass def mkarchive(dir,capacity=100*1024*1024): files = os.listdir(dir) totalsize = 0 for f in files:
totalsize = totalsize + os.getsize(os.path.join(dir,f)) if totalsize > capacity:
raise SizeError(dir)
for f in files:
shutil.copy(os.path.join(dir,f),os.path.join(’/zip’,f)) Note that the copy function may very well raise exceptions of its own; how- ever, since we’ve defined our own SizeError exception, we’ll be able to distinguish between the two types of error.
Now consider how we might use this function. Suppose that we’re going to create several archives, with a program that will prompt us to change disks between each archive. The following program would repeatedly call the mkarchive function, printing appropriate status messages after each call: dirs = [’/home/joe/papers’,’/home/sue/backup’,’/home/fred/save’] for d in dirs:
print ’Insert zip disk and hit return ’, sys.stdin.readline()
9.5. RAISING EXCEPTIONS 145
try:
mkarchive(dir)
print ’Archived %s to zip disk’ % dir except SizeError,msg:
print ’Directory %s too large for zip disk’ % msg except IOError,msg:
print ’Error archiving directory %s : %s’ % (dir,msg) Notice that the print statement after the call to mkarchive will only be executed if no exception was encountered in the function call.
While it’s natural to think of raising exceptions when an error is detected, exceptions can be used in other situations as well. Recall the walk function introduced in Section 8.6. This function will traverse a directory, calling a user-written function each time a new directory is encountered. Suppose we wish to write a program to search for a file with a particular name, but we’d like the program to stop searching as soon as it finds such a file. While we could exit the program entirely (by calling sys.exit), there’s no way to simply return from the walk function until all the directories have been traversed without exceptions. We could create an exception, raise that exception once the filename has been found, and put the call to walk inside a try/except loop. The following program implements that strategy.
import os class FoundException(Exception): pass def chkname(name,dir,files): if name in files: raise FoundException(os.path.join(dir,name)) name = ’errno.h’ startdir = ’/usr/include’ try: os.path.walk(startdir,chkname,name)
print ’%s not found starting at %s’ % (name,startdir) except FoundException,pathname:
A similar strategy can be employed to make a “panic” exit from inside of deeply-nested loops.
Chapter 10
Writing Modules
10.1
Introduction
In Python, a module is simply a file which contains Python code and which can be imported into some other program. Thus, if you have a collection of functions which you’d like to use in other python programs, you can simply put them all in one file, say myfunctions.py, and use the import statement to make those functions available to you. If your module is located in your working directory, Python will always find it. Otherwise, it must be located in Python’s search path. You can view the search path with the following Python statements:
import sys sys.path
Since sys.path is just a list, you can append the names of other directories to search onto this list before using an import statement, to allow you to access modules which are not on the search path. But if you keep your mod- ules in one location, a more useful approach may be to set the PYTHONPATH environmental variable to that location in the appropriate startup file for your operating system. That way, all the Python programs you run will be able to use your modules.