[Courses] [python] Lesson 8: Extras

Akkana Peck akkana at shallowsky.com
Fri Aug 5 20:38:33 UTC 2011


Lesson 8: Extras

In this lesson, I'll cover a bunch of 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. Iit's not too late to make requests!

Let's start with error handling.

================ 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 "<stdin>", line 1, in <module>
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! Then share it with the class,
and of course, feel free to ask questions along the way.



More information about the Courses mailing list