CSC207 Software Design
Lectures
Python Tools

Languages as Tools

Languages are just programming tools

Just as most sophisticated programming tools are really little languages

Theoretically equivalent to one another

Anything you can do in one, you can do in another

Although some things may be easier or harder

Take a look at how Python implements some of the things we've seen in Java

Graphs in Python

Same basic concept: a map of nodes to sets of reachable nodes

Represent sets in Python using dictionaries

Keys are set elements

All values are None (or zero, or something like that)

Many languages use this trick

class Graph:

    def __init__(self):
        self.data = {}

    def getNodes(self):
        return self.data.keys()

    def hasNode(self, node):
        return node in self.data

    def addNode(self, node, arcs=[]):
        self.addEmptyNode(node)
        self.addArcs(node, arcs)

    def removeNode(self, node):
        del self.data[node]
        for key in self.data:
            self.removeArc(key, node)

    ...etc...

    def __len__(self):
        return len(self.data)

Unit Testing

PyUnit is a Python version of JUnit

Test cases, test suites, GUI and text runners, etc.

To use:

Import unittest

Derive test classes from unittest.TestCase

Write testXYZ() methods using assert, failIf(), and failUnless()

Override setUp() and tearDown() if necessary

Sound familiar?

One difference: use unittest.main() to run all tests in a file

import sys, unittest

class SimpleTests(unittest.TestCase):

    def testLowerLower(self):
        assert 'a' < 'z'

    def testLowerUpper(self):
        assert 'a' < 'Z'

    def testUpperUpper(self):
        assert 'A' < 'Z'

    def testUpperLower(self):
        assert 'A' < 'a'

if __name__ == "__main__":
    unittest.main()

.F..
======================================================================
FAIL: testLowerUpper (__main__.SimpleTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "testing.py", line 14, in testLowerUpper
    assert 'a' < 'Z'
AssertionError
----------------------------------------------------------------------
Ran 4 tests in 0.000s

FAILED (failures=1)

Working With the Filesystem

Scripting languages like Python often used for:

Manipulating files

Running other programs

Can do all of these things with shell scripts

That is, by saving shell commands in a file, and executing that

But:

The shell doesn't offer much in the way of data structures

Unix shell scripts and Windows batch files are closely tied to particular operating systems

Compare Python:

Rich data structures

Its os module hides (most of) the differences between different operating systems

Constants In os Module

Constant Unix Windows Mac Meaning
name "posix" "nt" "mac" O/S name
curdir "." "." ":" Current directory
pardir ".." ".." "::" Parent directory
sep "/" "\\" "?" Path separator
linesep "\n" "\r\n" "\r" Line separator

Functions in os Module

chdir(path) Change current directory
listdir(path) List contents of directory
mkdir(path) Make a new directory
rmdir(path) Remove a directory
remove(path) Remove a file
rename(old, new) Rename a file or directory

The os.path Sub-module

split(path) Return front and last component of path
exists(path) Does something called path exist?
isdir(path) True if path is a directory
isfile(path) True if path is a file
getatime(path) Get last access time
getmtime(path) Get last modification time
getsize(path) Get size in bytes

Report Missing Files

Prints out command line arguments that aren't files

import sys, os
for path in sys.argv[1:]:
    if not os.path.exists(path) or not os.path.isfile(path):
        print path

Deleting Temporary Files

Given a list of directory names on the command line, deletes *.tmp in those directories

import sys, os
for dir in sys.argv[1:]:
    os.chdir(dir)
    contents = os.listdir(os.curdir)
    for name in contents:
        if os.path.isfile(name) and name[-4:].lower() == ".tmp":
        os.remove(name)

Another Way to Delete Temporary Files

Create full path name rather than changing directory

import sys, os
for dir in sys.argv[1:]:
    contents = os.listdir(dir)
    for name in contents:
        name = os.path.join(dir, name)
        if os.path.isfile(name) and name[-4:].lower() == ".tmp":
            os.remove(name)

Find Old Files

Lists files that are older than N days

Where N is specified on the command line

And "older" means "haven't been accessed"

Uses time module to get the current time

And exceptions

Just like Java

import sys, os, time

try:
    days = int(sys.argv[1])
except IndexError:
    print >> sys.stderr, "Usage: oldfiles numdays dirs..."
    sys.exit(1)
except ValueError:
    print >> sys.stderr, "Can't parse '" + sys.argv[1] + "' as integer"
    sys.exit(1)

now = int(time.time())
cutoff = now - (days * 24 * 60 * 60)

for arg in sys.argv[2:]:
    if os.path.isdir(arg):
        contents = os.listdir(arg)
        for name in contents:
            name = os.path.join(arg, name)
            if os.path.isfile(name):
                if os.path.getatime(name) <= cutoff:
                    print name

An Exercise for the Reader

This script prints out files that are:

Older than N days if -o N used

Newer than N days if -n N used

New features:

Uses getopt module to parse command-line options

Assigns functions to variables

After all, a function is just a block of bytes...

from module import name to get specific functions

Your mission: understand how this script works

import sys, time, getopt
from os import listdir
from os.path import isdir, isfile, join, getatime

def older(t, cutoff):
    return t <= cutoff

def newer(t, cutoff):
    return t >= cutoff

days = 1
compare = older
optlist, dirs = getopt.getopt(sys.argv[1:], "o:n:")
for (opt, arg) in optlist:
    if opt == "-o":
        compare = older
        days = int(arg)
    elif opt == "-n":
        compare = newer
        days = int(arg)

now = int(time.time())
cutoff = now - (days * 24 * 60 * 60)

for d in dirs:
    if isdir(d):
        contents = listdir(d)
        for path in contents:
            path = join(d, path)
            if isfile(path) and compare(getatime(path), cutoff):
                print path

Running Other Programs

One program can run another

Make does this

So does the shell

And so can Python

This will be a major topic of 209

Give you a sneak preview here

Reading from a Sub-Command

Goal: run a program, and read its output as if it were a file

from os import popen
input = popen("ls", "r")
for line in input.readlines():
    print line

deletejoin.py

deletetemp.py

etc...

Note: double-spaced because the lines we are reading have carriage returns

Can write to a sub-command by opening with "w" instead of "r"


$Id: pytools.html,v 1.1.1.1 2004/01/04 05:02:31 reid Exp $