Python talk material

This page contains HTML renditions of slides from an introductory talk on Python I've given in the past. Some of it has been updated and/or tweaked, but not since the last change date: 5/23/97.

Much of this material comes from the book "Programming Python". Since the pages here lack the narrative you'd get in a talk (or the book), I'm not sure how useful this is as an intro to Python; on the other hand, it's free :-).

Please send comments about this page to lutz@learning-python.com.



Content links



Also see the appendix page for supplemental material. Some of it is specific to a company I've worked for, but it also includes some general Python information not listed above:

Update: also see the new integration examples page for more Python/C integration examples.





 "And now,

  for something

  completely different..."





Mark Lutz
March, 1997


Talk outline


But first: the book plug...




[O'Reilly book cover]




I. General Overview



So what's Python?



Major features



Some quotable quotes



A more formal definition

Seen on comp.lang.python...

"python, (Gr. Myth. An enormous serpent that lurked in the cave of Mount Parnassus and was slain by Apollo) 1. any of a genus of large, non-poisonous snakes of Asia, Africa and Australia that crush their prey to death. 2. popularly, any large snake that crushes its prey. 3. totally awesome, bitchin' language invented by that rad computer geek Guido van Rossum that will someday crush the $'s out of certain *other* so-called VHLLs ;-)"


The Life Of Python



What's Python good for?



What's Python not good for?



The Compulsory Features List


Python Portability



General portability



Platform Extensions



The PythonWin IDE

[PythonWin IDE]


Major Python Packages

A first look: the Grail web browser in action


HTML file

<HEAD>
<TITLE>Grail Applet Test Page</TITLE>
</HEAD>

<BODY>
<H1>Test an Applet Here!</H1>
Click this button!
<APP CLASS=Question>
</BODY>


file: Question.py

# Python applet file: Question.py
# in the same location (URL) as the html file 
# that references it; adds widgets to browser;

from Tkinter import *

class Question:                          # run by grail?
    def __init__(self, master):          # master=browser
        self.button = Button(master,
                             bitmap='question',
                             command=self.action)
        self.button.pack()

    def action(self):
        if self.button['bitmap'] == 'question':
            self.button.config(bitmap='questhead')
        else:
            self.button.config(bitmap='question')

if __name__ == '__main__': 
    root = Tk()                    # run stand-alone?
    button = Question(root)        # master=Tk: top-level
    root.mainloop()



[Grail web browser]




WPY at work



[Scribble under WPY]



Python in the "Real World"



On Bananas and Coconuts

Python versus...

But YMMV!



II. Python Resources



Internet Resources



Python Books

Also useful



Recent Python Articles



PSA-sponsored Python Conferences



Python Training



III. A first look at the language



How to Run Python Programs



UNIX scripts

	file: brian.py
	#!/usr/local/bin/python
	print 'The Bright Side of Life...'

	% brian.py

Module Files

	% python spam.py -i eggs -o bacon 

Interactive command line

	% python
	>>> print 'Hello world!'
	Hello world!
	>>> lumberjack = "okay"

Embedded Code/Objects

	Py_Initialize();
	PyRun_SimpleString("x = brave + sir + robin");


Notes: on UNIX, the environment variable PYTHONPATH should list all source directories you want to import modules from. Program start techniques and configuration details differ on Windows and Macintosh platforms; see package documentation.


Builtin object types





Numbers

Common operations

1234               normal integers (C long's) 
99999999L          long integers (unlimited size)
1.23, 3.14e-10     floating-point (C double's)
0177, 0x9ff        octal and hex constants

Interactive examples

% python
>>> a = 3                                       
>>> b = 4                                       
>>> b / 2 + a                                   
5
>>> b / (2.0 + a)
0.8  
>>> 9999999999999999999999999999 + 1            
OverflowError: integer literal too large        
>>> 9999999999999999999999999999L + 1           
10000000000000000000000000000L                  


Strings

Common operations

s1 = ''                     empty strings
s2 = "spam's"               double quotes
s2[i], s2[i:j]              indexing, slicing
s1 + s2, s2 * 3             concatenate, repeat
"a %s parrot" % 'dead'      string formatting
block = """..."""           triple-quoted blocks

Interactive examples

% python
>>> 'abc' + 'def'          
'abcdef'                   
>>> 'Ni!' * 4          # like "Ni!" + "Ni!" + ...
'Ni!Ni!Ni!Ni!'                    
>>> S = 'spam'
>>> S[0], S[-2]        # same as:   S[len(S)-2]      
('s', 'a')                
>>> S[:-1], S[1:]         
('spa', 'pam')              
>>> 'That is %d %s bird!' % (1, 'dead')
That is 1 dead bird!


Lists

Common operations

L1 = []                     an empty list
L2 = [0, 1, 2, 3]           4-items: indexes 0..3
['abc', ['def', 'ghi']]     nested sublists
L2[i], L2[i:j]              indexing, slicing
L1 + L2, L2 * 3             concatenate, repeat
L2.append(newvalue)         growing: methods
del L2[k], L2[i:j] = []     shrinking
L2[i:j] = [1,2,3]           slice assignment
range(4), xrange(0, 4)      make integer lists

Interactive examples

% python
>>> [1, 2, 3] + [4, 5, 6]      
[1, 2, 3, 4, 5, 6]         
>>> ['Ni!'] * 4            
['Ni!', 'Ni!', 'Ni!', 'Ni!']  
>>> L = ['spam', 'Spam', 'SPAM!']
>>> L[2]               
'SPAM!'
>>> L[1] = 'eggs'
>>> for x in L: print x,                        
...                                    
spam eggs SPAM!                                   


Dictionaries

Common operations

d1 = {}                                 empty 
d2 = {'spam': 2, 'ham': 1, 'eggs': 3}   3-items
d3 = {'pork': {'ham': 1, 'brats': 2}}   nesting
d2['eggs'], d3['pork']['ham']           indexing
d2.has_key('eggs'), d2.keys()           methods

Interactive examples

% python
>>> table ={'Perl':    'Larry Wall',		
...         'Tcl':     'John Ousterhout',
...         'Python':  'Guido van Rossum' }
...
>>> language = 'Python'
>>> creator  = table[language]
'Guido van Rossum'


Tuples

Common operations

()                            an empty tuple
t1 = (0,)                     a one-item tuple
t2 = (0, 1, 2, 3)             4-item tuple
t3 = ('abc', ('def', 'ghi'))  nested tuples
t1[i], t1[i:j]                index, slice
t1 + t2, t2 * 3               concatenate, repeat


Files

Common operations

o = open("/tmp/spam", 'w')         output file
i = open('data', 'r')              input file
i.read(), i.readline(), i.read(1)  file,line,char
o.write(s), o.writelines(l)        string,lines
o.close()                          or when free'd


And everything else...

In Python, everything is an object type.



General properties

Nesting example

	L = ['abc', [(1,2), ([3], 4)], 5]          


[Nested data structures]


Basic statements

Python syntax...

Python assignment...

Python programming...


Simple statements



Assignment

spam = 'SPAM'                  basic form
spam, ham = 'yum', 'YUM'       tuple assignment
[spam, ham] = ['yum', 'YUM']   list assignment
spam = ham = 'lunch'           multi-target


Expressions

spam(egs, ham)                 function calls
spam.ham(eggs)                 method calls
spam                           print interactive 
spam < ham and ham != eggs     compound expr's
spam < ham < eggs              range tests


Print

print spam, ham              print objects to sys.stdout
print spam, ham,             don't add linefeed



Compound statements



If selections

>>> x = 'killer rabbit'
>>> if x == 'bunny':               
...     print 'hello little bunny'
... elif x == 'bugs':
...     print "what's up doc?"                    
... else:                    
...     print 'Run away!  Run away!...' 
...                          
Run away!  Run away!...                 


While loops

while 1: 
    print 'Type Ctrl-C to stop me!'


The loop "else"...

x = y / 2
while x > 1:
    if y % x == 0:                 # remainder
        print y, 'has factor', x
        break                      # skip else
    x = x-1
else:                              # normal exit
    print y, 'is prime'


For loops

>>> for x in ["spam", "eggs", "spam"]:
...     print x,
...
spam eggs spam


Counter loops...

>>> range(5)      
[0, 1, 2, 3, 4]   
>>> for i in xrange(len(X)): print X[i]     

>>> for i in range(5): print 'A shrubbery!'     
...                                             
A shrubbery!                                    
A shrubbery!                                    
A shrubbery!                                    
A shrubbery!                                    
A shrubbery!                                    



Program unit statements


Modules contain Statements that process Objects

[Module relationships]



Functions


def intersect(seq1, seq2):
    res = []                     # start empty
    for x in seq1:               # scan seq1
        if x in seq2:            # common item?
            res.append(x)        # add to end
    return res



>>> s1 = "SPAM"
>>> s2 = "SCAM" 

>>> intersect(s1, s2)               # strings
['S', 'A', 'M']

>>> intersect([1, 2, 3], (1, 4))    # mixed types
[1]



Modules


file: hello.py

def printer(x):
    print x



% python
>>> import hello
>>> hello.printer('Hello world!')
Hello world!   

>>> from hello import printer
>>> printer('Hello world!')     

>>> from hello import *
>>> printer('Hello world!')



Classes


class FirstClass:                   # class
    def printer(self, text):        # method
        print text

x = FirstClass()
x.printer('Hello world!')


class SecondClass(FirstClass):      # subclass
    def __init__(self, value):      # constructor
        self.name = value
    def printname(self):
        self.printer(self.name)   	  # inherited
	 
x = SecondClass('Hello world!')
x.printname()



More on Python's OOP model


class Counter:
    def __init__(self, start):          # on Counter()
        self.data = start
    def __call__(self):                 # on x()
        self.data = self.data + 1
        return self.data
    def __add__(self, y):               # on x + y
        return Counter(self.data + y)
    def __repr__(self):                 # on print
        return `self.data`


if __name__ == "__main__":
    x = Counter(2)                # self-test
    x = x + 4
    print x                       # 6

    c1 = Counter(10)
    c2 = Counter(24)
    print c1(), c2(), c1(), c2()  # 11, 25, 12, 26



Exceptions


Builtin exceptions...

def kaboom(list, n):
    print list[n]            # trigger IndexError

try:
    kaboom([0, 1, 2], 3)
except IndexError:
    print 'Hello world!'     # catch exception here



User-defined exceptions...

MyError = "my error"
def stuff(file): 
    raise MyError

file = open('data', 'r')     # open a file
try:
    stuff(file)              # raises exception
finally:
    file.close()             # always close file


A pragmatic interlude: system interfaces



Python tools: sys

   >>> import sys
   >>> sys.path
   ['.', '/usr/local/lib/python', ... ]
   >>> sys.platform
   'sunos4'

System tools: os

   >>> import os
   >>> os.environ['USER']   
   'mlutz'
   >>> listing = os.popen("ls *.py").readlines()
   >>> for name in listing: print name,
   ... 
   cheader1.py
   finder1.py
   summer.py   

   >>> for name in listing: os.system("vi " + name)
   ... 
   >>> os.listdir(".")
   ['summer.out', 'summer.py', 'table1.txt', ... ]

File globbing: glob, string

   >>> import glob, string, os
   >>> glob.glob("*.py")
   ['cheader1.py', 'finder1.py', 'summer.py']

   >>> for name in glob.glob("*.py"): 
   ...     os.rename(name, string.upper(name))
   ... 
   >>> glob.glob("*.PY")
   ['FINDER1.PY', 'SUMMER.PY', 'CHEADER1.PY']

Arguments and streams: sys

   % cat play.py
   #!/usr/local/bin/python
   import sys
   print sys.argv
   sys.stdout.write("ta da!\n")
    
   % play.py -x -i spammify
   ['play.py', '-x', '-i', 'spammify']
   ta da!


OOP and inheritance

A zoo-animal hiearchy in Python

file: zoo.py

class Animal:
    def reply(self):   self.speak()
    def speak(self):   print 'spam'

class Mammal(Animal):
    def speak(self):   print 'huh?'

class Cat(Mammal):
    def speak(self):   print 'meow' 

class Dog(Mammal):
    def speak(self):   print 'bark'

class Primate(Mammal):
    def speak(self):   print 'Hello world!'

class Hacker(Primate): pass  			



% python
>>> from zoo import Cat, Hacker
>>> spot = Cat()
>>> spot.reply()
meow
>>> data = Hacker()
>>> data.reply()
Hello world!


[Zoo animal hierarchy]



OOP and composition

The dead-parrot skit in Python

file: parrot.py

class Actor:
    def line(self): print self.name + ':', `self.says()`

class Customer(Actor):
    name = 'customer'
    def says(self): return "that's one ex-bird!"

class Clerk(Actor):
    name = 'clerk'
    def says(self): return "no it isn't..."
 
class Parrot(Actor):
    name = 'parrot'
    def says(self): return None

class Scene:
    def __init__(self):
        self.clerk    = Clerk()       # embed some instances
        self.customer = Customer()    # Scene is a composite
        self.subject  = Parrot()

    def action(self):
        self.customer.line()          # delegate to embedded
        self.clerk.line()
        self.subject.line()


% python
>>> import parrot                
>>> parrot.Scene().action()      
customer: "that's one ex-bird!"     
clerk: "no it isn't..."       
parrot: None                     


[Dead parrot skit]



Functions are objects: indirect calls

def echo(message): print message

x = echo	
x('Hello world!')

def indirect(func, arg): 
    func(arg)
indirect(echo, 'Hello world!')

schedule = [ (echo, 'Hello!'), (echo, 'Ni!') ]
for (func, arg) in schedule:
    apply(func, (arg,))

File scanners

file: scanfile.py

def scanner(name, function): 
    file = open(name, 'r')          # create file
    for line in file.readlines():
        function(line)              # call function
    file.close() 


file: commands.py

import string
from scanfile import scanner

def processLine(line):  
    print string.upper(line)

scanner("data.txt", processLine)    # start scanner


Classes are objects: factories

def factory(aClass, *args):        # varargs tuple
    return apply(aClass, args)     # call aClass

class Spam:
    def doit(self, message):
        print message

class Person:
    def __init__(self, name, job):
        self.name = name
        self.job  = job

object1 = factory(Spam)
object2 = factory(Person, "Guido", "guru")


Methods are objects: bound or unbound

x = object1.doit        # bound method object
x('hello world')        # instance is implied

t = Spam.doit           # unbound method object
t(object1, 'howdy')     # pass in instance 


Modules are objects: metaprograms

file: fixer.py

editor = 'vi'  # your editor's name

def python(cmd):
    import __main__
    namespace = __main__.__dict__
    exec cmd in namespace, namespace

def edit(filename):
    import os
    os.system(editor + ' ' + filename)

def fix(modname):                            
    import sys                      # edit,(re)load
    edit(modname + '.py')
    if modname in sys.modules.keys():
        python('reload(' + modname + ')')
    else:
        python('import ' + modname)


% python
>>> from fixer import fix
>>> fix("spam")                  	# browse/edit and import by string name
>>> spam.function()                	# spam was imported in __main__
>>> fix("spam")                       	# edit and reload() by string name
>>> spam.function()               	# test new version of function

Python/C integration

[See the appendix for examples]



Extending Python in C



Embedding Python in C



Going both ways

[Integration flows]



Python and rapid development


Summary: Python tool-set layers



IV. Real Python Programs



Simple shell tools



Packing files with simple scripts



% cat packer.py

#!/usr/local/bin/python
import sys                    # load the system module
marker = '::::::'

for name in sys.argv[1:]:     # for all command-line args
    input = open(name, 'r')   # open the next input file
    print marker + name       # write a separator line
    print input.read(),       # write the file's contents



% packer.py spam.txt eggs.txt toast.txt  >  packed.txt


Packing files with a class framework



% cat packapp.py

#!/usr/local/bin/python
from apptools import StreamApp       # get the superclass
from textpack import marker          # get marker constant

class PackApp(StreamApp):            # define a subclass
    def start(self):                 # define some methods
        if not self.args:
            self.exit('packapp.py [-o file]? src src...')
        
    def help(self):
        StreamApp.help(self)         # superclass args
        print '<file> <file>...'     # then show my args 

    def run(self):
        for name in self.restargs():
            try:
                self.message('packing: ' + name)
                self.pack_file(name)
            except:
                self.exit('error processing: ' + name)
    
    def pack_file(self, name):  
        self.setInput(name)           
        self.write(marker + name + '\n')
        while 1:
            line = self.readline()       # read next line
            if not line: break           # until eof
            self.write(line)             # copy to output

if __name__ == '__main__':  PackApp().main()    # script?


% packapp.py -o packed.txt spam.txt eggs.txt toast.txt

GUI programming



file: hello
#!/usr/local/bin/python
from Tkinter import *                   # get widget classes

class Hello(Frame):                     # container subclass
    def __init__(self, parent=None):   
        Frame.__init__(self, parent)    # do superclass init
        self.pack()
        self.make_widgets()

    def make_widgets(self):
        widget = Button(self,
                        text='Hello world',
                        command = self.onPress)
        widget.pack(side=LEFT)

    def onPress(self): 
        print "Hi."

if __name__ == '__main__':  Hello().mainloop()



% hello


[Hello GUI world]


Persistent Python objects



file: person.py

# a person object: fields + behavior
# class defined at outer level of file

class Person:
    def __init__(self, name = '', job = '', pay = 0):
        self.name = name
        self.job  = job
        self.pay  = pay               # real instance data
    def tax(self):
        return self.pay * 0.25        # computed on demand
    def info(self):
        return self.name, self.job, self.pay, self.tax()



% python
>>>jerry = Person('jerry', 'dentist')
>>>bob   = Person('bob', 'psychologist', 70000)
>>>emily = Person('emily', 'teacher', 40000)
...
>>>import shelve
>>>dbase = shelve.open('cast')          # open a new dbm file
>>>for obj in (bob, emily, jerry):      # put objs in a shelve
>>>    dbase[obj.name] = obj
...
>>>import shelve              
>>>dbase = shelve.open('cast')           # reopen shelve file
>>>print dbase['bob'].info()             # fetch 'bob' object

Fun with Sets



Set functions

file: inter.py

def intersect(list1, list2):
    res = []                      # start with an empty list
    for x in list1:               # scan the first list
        if x in list2:
            res.append(x)         # add common items to end
    return res

def union(list1, list2):				
    res = map(None, list1)        # make a copy of list1
    for x in list2:               # add new items in list2
        if not x in res:
            res.append(x)
    return res



% python
>>> from inter import *
>>> s1 = "SPAM"
>>> s2 = "SCAM" 

>>> intersect(s1, s2), union(s1, s2)           # strings
(['S', 'A', 'M'], ['S', 'P', 'A', 'M', 'C'])

>>> intersect([1,2,3], (1,4))                  # mixed types
[1]
>>> union([1,2,3], (1,4))
[1, 2, 3, 4]


Set classes

file: set.py

class Set:
    def __init__(self, value = []):    # constructor
        self.data = []                 # manages a list
        self.concat(value)

    def intersect(self, other):        # other is a sequence 
        res = []                       # self is the subject
        for x in self.data:
            if x in other:
                res.append(x)
        return Set(res)                # return a new Set

    def union(self, other):				
        res = self.data[:]             # copy of my list
        for x in other:                                    
            if not x in res:
                res.append(x)
        return Set(res)				

    def concat(self, value):           # value: list, Set...
        for x in value:                # removes duplicates
           if not x in self.data:
                self.data.append(x)

    def __len__(self):          return len(self.data)
    def __getitem__(self, key): return self.data[key] 	
    def __and__(self, other):   return self.intersect(other)
    def __or__(self, other):    return self.union(other)
    def __repr__(self):         return 'Set:' + `self.data`



% python
>>> from set import Set
>>> x = Set([1,2,3,4])             # __init__
>>> y = Set([3,4,5])
>>> x & y, x | y                   # __and__,__or__,__repr__
(Set:[3, 4], Set:[1, 2, 3, 4, 5])

>>> z = Set("hello")               # set of strings
>>> z[0]                           # __getitem__
'h'
>>> z & "mello", z | "mello"
(Set:['e', 'l', 'o'], Set:['h', 'e', 'l', 'o', 'm'])


Multiple operands: *varargs

file: inter2.py

def intersect(*args):
    res = [] 
    for x in args[0]:                  # scan first list
        for other in args[1:]:         # for all other args
            if x not in other: break   # this in each one?
        else:
            res.append(x)              # add items to end
    return res

def union(*args):				
    res = []
    for seq in args:                   # for all args
        for x in seq:                  # for all nodes
            if not x in res: 
                res.append(x)          # add items to result
    return res



% python
>>> from inter2 import *
>>> s1, s2, s3 = "SPAM", "SCAM", "SLAM" 

>>> intersect(s1, s2), union(s1, s2)            # 2 operands
(['S', 'A', 'M'], ['S', 'P', 'A', 'M', 'C'])

>>> intersect([1,2,3], (1,4))
[1]

>>> intersect(s1, s2, s3)                       # 3 operands
['S', 'A', 'M']

>>> union(s1, s2, s3)
['S', 'P', 'A', 'M', 'C', 'L']

The Python FTP module

#!/usr/local/bin/python
############################################### 
# Usage: % sousa.py
# Fetch and play the Monty Python theme song.
###############################################
 
import os, sys
from ftplib import FTP             # socket-based ftp tools
from posixpath import exists       # file existence test
 
sample = 'sousa.au'
filter = {'sunos5': '/usr/bin/audioplay', 
          'linux1': '',
          'sunos4': '/usr/demo/SOUND/play'}

helpmsg = """
Sorry: can't find an audio filter for your system!
Add an entry to the script's "filter" dictionary for
your system's audio command, or ftp and play manually.
"""

# check the filter 
if (filter.has_key(sys.platform) and 
    exists(filter[sys.platform])):
    print 'Working...'
else:
    print helpmsg    
    sys.exit(1)

# ftp the audio file
if not exists(sample):
    theme = open(sample, 'w')
    ftp = FTP('ftp.python.org')       # connect to ftp site
    ftp.login()                       # use anonymous login
    ftp.cwd('pub/python/misc')
    ftp.retrbinary('RETR ' + sample, theme.write, 1024)
    ftp.quit()
    theme.close()
 
# send it to audio device
theme = open(sample, 'r')             
audio = os.popen(filter[sys.platform], 'w')    # spawn tool
audio.write(theme.read())                      # to stdin

Sockets in Python



file: sockserver.py

# Echo server program
from socket import *
HOST = ''                     # the local host
PORT = 50007                  # non-privileged server

s = socket(AF_INET, SOCK_STREAM)
s.bind(HOST, PORT)
s.listen(1)
conn, addr = s.accept()
print 'Connected by', addr

while 1:
    data = conn.recv(1024)
    if not data: break
    conn.send(data)
conn.close()



file: sockclient.py

# Echo client program
from socket import *
HOST = 'daring.cwi.nl'          # the remote host
PORT = 50007                    # the port used by server

s = socket(AF_INET, SOCK_STREAM)
s.connect(HOST, PORT)
s.send('Hello, world')

data = s.recv(1024)
s.close()
print 'Received', `data`

Text processing: regular expressions

file: cheader1.py
#! /usr/local/bin/python
import sys, regex
from string import strip

pattDefine = regex.compile(
    '^#[\t ]*define[\t ]+\([a-zA-Z0-9_]+\)[\t ]*')  

pattInclude = regex.compile(
    '^#[\t ]*include[\t ]+[<"]\([a-zA-Z0-9_/\.]+\)') 

def scan(file):
    count = 0
    while 1:                             # scan line-by-line
        line = file.readline()
        if not line: break
        count = count + 1
        n = pattDefine.match(line)       # save match length
        if n >= 0:
            name = pattDefine.group(1)   # matched substring
            body = line[n:]
            print count, name, '=', strip(body)
        elif pattInclude.match(line) >= 0:
            regs = pattInclude.regs            # start,stop
            a, b = regs[1]                     # of group 1
            filename = line[a:b]               # slice out
            print count, 'include', filename

if len(sys.argv) == 1:
    scan(sys.stdin)                 # no args: read stdin
else:
    scan(open(sys.argv[1], 'r'))    # arg: input file name



% cheader1.py test.h
1 include stdio.h
2 include usr/local/include/Py/Python.h
4 SPAM = 
5 SHOE_SIZE = 7.5
7 include local_constants.h
8 PARROT = dead + bird

Text processing: splitting files by columns



file: summer.py

import string, sys

def summer(numCols, fileName):
    sums = [0] * numCols                        
    for line in open(fileName, 'r').readlines():
        cols = string.split(line)               
        for i in range(numCols):               
            sums[i] = sums[i] + eval(cols[i])
    return sums

if __name__ == '__main__':
    print summer(eval(sys.argv[1]), sys.argv[2]) 



% summer.py cols file

System interfaces: regression tests

#!/usr/local/bin/python
import os, sys                   # get unix, python services 
from stat import ST_SIZE         # file stat record 
from glob import glob            # file-name expansion
from posixpath import exists     # file exists test
from time import time, ctime     # time functions

print 'RegTest start.' 
print 'user:', os.environ['USER']    # environment variables
print 'path:', os.getcwd()           # current directory
print 'time:', ctime(time()), '\n'
program = sys.argv[1]                # two command-line args
testdir = sys.argv[2]

for test in glob(testdir + '/*.in'):    # for all *.in files
    if not exists(test + '.out'):
        # no prior results
        command = '%s < %s > %s.out 2>&1'
        os.system(command % (program, test, test))
        print 'GENERATED:', test

    else: 
        # backup, run, compare
        os.rename(test + '.out', test + '.out.bkp')
        command = '%s < %s > %s.out 2>&1'
        os.system(command % (program, test, test))
        command = 'diff %s.out %s.out.bkp > %s.diffs'
        os.system(command % ((test,)*3) )
        if os.stat(test + '.diffs')[ST_SIZE] == 0:
            print 'PASSED:', test 
            os.unlink(test + '.diffs')
        else:
            print 'FAILED:', test, '(see %s.diffs)' % test
    
print 'RegTest done:', ctime(time())



% regtest.py shrubbery test1
RegTest start.
user: mark
path: /home/mark/stuff/python/testing
time: Mon Feb 26 21:13:20 1996

FAILED: test1/t1.in (see test1/t1.in.diffs)
PASSED: test1/t2.in
RegTest done: Mon Feb 26 21:13:27 1996

Other things that weren't covered


And finally...



"Nobody expects 

 the Spanish Inquisition..."




Questions?

Copyright 1997, Mark Lutz


[Python Logo] Books Python Code Blog About Email Train Main Search Top ©M.Lutz