Lesson 8: Extras In this lesson, I'll cover a bunch of little useful stuff that didn't quite fit in the earlier lessons, plus some topics people asked about. There will be at least one more extras lesson after this one, including a bit about GUI frameworks and a couple other tips and tricks people have requested -- it's not too late to make requests. ================ Exceptions: Handling errors ================== Of course you try to write code that won't have errors. But sometimes errors happen anyway, and your code has to deal with them. Python has a nice mechanism for catching errors, called exceptions. It works like this: try : filename = "/no/such/file" myfile = open(filename) for line in myfile : print line except IOError, err : print "Couldn't open", filename print "Error was:", err print "Done, whether or not an error happened" try means: Here's a block of code that might bomb out in some way. Try to run it, and if something goes wrong, look for a matching 'except' clause and do that. Then you have an except with the type of error you expect to get, and the name of a variable where Python will put the error info. How do you know? Try running it without the try/except and see what Python prints: >>> filename = "/no/such/file" >>> myfile = open(filename) Traceback (most recent call last): File "", line 1, in IOError: [Errno 2] No such file or directory: '/no/such/file' That tells me that if the file doesn't exist, an IOError is the type of error you'll get. If you get some other error, maybe one that has nothing to do with opening the file, it'll bomb out just like it would have otherwise. Sometimes you want to handle EVERY type of error. You don't have to specify an exception type or a variable at all: except : print "Some kind of error occurred" Or you might want to handle every error, but still have a variable to keep track of what type of error it was. In that case, use the general Exception type: except Exception, err : print "Some type of error occurred:", err You can handle several types of errors for the same try: try : myfile = open(filename) for line in myfile : i = 12 / 0 print line except IOError, err : print "Couldn't open", filename print "Error was:", err except Exception, err : print "Error that wasn't an IOError:", err Of course, the Exception class and its subclasses have lots of detailed information you can get about the error. See http://docs.python.org/tutorial/errors.html if you're curious about the details. ================ Optional arguments ======================= You can have functions with optional arguments. They look like this: def greet(firstname, lastname, language="python", os=None) : print "Welcome,", firstname, lastname print "I see you program in", language if os : print "and that you use", os Then you could call it in lots of ways: greet("Linus", "Torvalds") greet("Linus", "Torvalds", "c", "Linux") greet("Linus", "Torvalds", language="c", os="Linux") greet("Linus", "Torvalds", os="Linux") Any argument that doesn't have a = is required -- you'll get an error if you don't provide it. But if you give a default with an = after the argument, that argument becomes optional, and if it's not supplied, Python will fill it in with the default. And you can specify arguments out of order when you call the function, by using the name of the argument. ================ Running system commands ======================= Peggy asked a great question on how to capture output of a system command. Python can be an excellent replacement for shell scripts -- but only if you can call arbitrary system commands. And if you google that, it's a little confusing, because there are several different ways and some of them are deprecated. So here's what I use: First, if you just want to run a simple command, that's easy: import os os.system("motd") os.system("ls -l " + os.getenv("HOME)) If you need the output, that's only a little harder. You can use popen: fp = os.popen('find . -name "*.jpg"') for line in fp: print line.strip() If you want to build up a command with arguments, it gets a little trickier. Of course you can just build up the same commandline you might type from the shell. Like if iface is "eth0" and I need to call ifconfig eth0 up: os.system("ifconfig " + iface + " up") or, better, os.system("ifconfig %s up" % iface) ============ A slightly harder, but better, way =============== The problem with os.system and os.popen is that you're building up a full command line to pass to a subshell -- and there's a security risk there, depending on where you get your arguments. In my network interface example, suppose I asked the user which interface to configure, and instead of saying "eth0", she said "`rm -rf /`". The program would obligingly pass that to os.system/os.popen, the shell would run the part in backquotes, and you've lost your filesystem. Not so good! So in any nontrivial program, I use the subprocess module. Just running a program (like os.system) is very easy: pass it a list. import subprocess subprocess.call(["ifconfig", iface, "up"]) Capturing output is a little harder, because there are several different syntaxes. This is probably the easiest: p = subprocess.Popen(["find", ".", "-name", "*.jpg"], stdout=subprocess.PIPE) for line in p.stdout : print line For lots more detail on subprocess and all the things it can do, see http://docs.python.org/library/subprocess.html#subprocess-replacements ====================== Homework ========================== No formal homework assignment this time; just ideas for projects. You might go back into your previous homework solutions and add some error handling using exceptions, and add functions that take optional arguments. But for those who have time, here are some suggestions: Write a python script to do something you'd normally do with a shell script, maybe something involving using find or locate to search for files on the filesystem, or using grep to search within a file. Include some exception handling, and use functions (with optional arguments if appropriate). For instance, I organize my photos in directories by date, and each directory has a file called Keywords. Then I have a python script to search through all my photos and find all the photos of butterflies, or cars, or signs. I have another program that searches through files to see which ones are are a particular language, like Python (anything that has a "#! ... python" as its first line), then grep only in those. So if I'm writing a lesson about Popen, I can say langgrep python Popen and it'll show me all my Python programs where I've used Popen. Another useful program is one that takes all the JPG image files in the current directory and uses imagemagick commands like convert and mogrify to create a thumbnail for each image, then build a "photo gallery" web page from all the images. But don't necessarily copy one of these ideas -- find something you'd find useful yourself, and write it! And feel free to ask questions along the way.