Merged revisions 74609,74627,74634,74645,74651,74738,74840,75016,75316-75317,75323-75324,75326,75328,75330,75338,75340-75341,75343,75352-75353,75355,75357,75359 via svnmerge from
svn+ssh://svn.python.org/python/branches/py3k

................
  r74609 | senthil.kumaran | 2009-08-31 18:43:45 +0200 (Mo, 31 Aug 2009) | 3 lines

  Doc fix for issue2637.
................
  r74627 | georg.brandl | 2009-09-02 22:31:26 +0200 (Mi, 02 Sep 2009) | 1 line

  #6819: fix typo.
................
  r74634 | georg.brandl | 2009-09-03 14:34:10 +0200 (Do, 03 Sep 2009) | 9 lines

  Merged revisions 74633 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r74633 | georg.brandl | 2009-09-03 14:31:39 +0200 (Do, 03 Sep 2009) | 1 line

    #6757: complete the list of types that marshal can serialize.
  ........
................
  r74645 | georg.brandl | 2009-09-04 10:07:32 +0200 (Fr, 04 Sep 2009) | 1 line

  #5221: fix related topics: SEQUENCEMETHODS[12] doesnt exist any more.
................
  r74651 | georg.brandl | 2009-09-04 13:20:54 +0200 (Fr, 04 Sep 2009) | 9 lines

  Recorded merge of revisions 74650 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r74650 | georg.brandl | 2009-09-04 13:19:34 +0200 (Fr, 04 Sep 2009) | 1 line

    #5101: add back tests to test_funcattrs that were lost during unittest conversion, and make some PEP8 cleanups.
  ........
................
  r74738 | georg.brandl | 2009-09-09 18:51:05 +0200 (Mi, 09 Sep 2009) | 9 lines

  Merged revisions 74737 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r74737 | georg.brandl | 2009-09-09 18:49:13 +0200 (Mi, 09 Sep 2009) | 1 line

    Properly document copy and deepcopy as functions.
  ........
................
  r74840 | georg.brandl | 2009-09-16 18:40:45 +0200 (Mi, 16 Sep 2009) | 13 lines

  Merged revisions 74838-74839 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r74838 | georg.brandl | 2009-09-16 18:22:12 +0200 (Mi, 16 Sep 2009) | 1 line

    Remove some more boilerplate from the actual tests in test_pdb.
  ........
    r74839 | georg.brandl | 2009-09-16 18:36:39 +0200 (Mi, 16 Sep 2009) | 1 line

    Make the pdb displayhook compatible with the standard displayhook: do not print Nones. Add a test for that.
  ........
................
  r75016 | georg.brandl | 2009-09-22 15:53:14 +0200 (Di, 22 Sep 2009) | 1 line

  #6969: make it explicit that configparser writes/reads text files, and fix the example.
................
  r75316 | georg.brandl | 2009-10-10 23:12:35 +0200 (Sa, 10 Okt 2009) | 9 lines

  Merged revisions 75313 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r75313 | georg.brandl | 2009-10-10 23:07:35 +0200 (Sa, 10 Okt 2009) | 1 line

    Bring old demo up-to-date.
  ........
................
  r75317 | georg.brandl | 2009-10-10 23:13:21 +0200 (Sa, 10 Okt 2009) | 9 lines

  Merged revisions 75315 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r75315 | georg.brandl | 2009-10-10 23:10:05 +0200 (Sa, 10 Okt 2009) | 1 line

    Remove unneeded "L" suffixes.
  ........
................
  r75323 | georg.brandl | 2009-10-10 23:48:05 +0200 (Sa, 10 Okt 2009) | 9 lines

  Recorded merge of revisions 75321 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r75321 | georg.brandl | 2009-10-10 23:43:21 +0200 (Sa, 10 Okt 2009) | 1 line

    Remove outdated comment and fix a few style issues.
  ........
................
  r75324 | georg.brandl | 2009-10-10 23:49:24 +0200 (Sa, 10 Okt 2009) | 9 lines

  Merged revisions 75322 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r75322 | georg.brandl | 2009-10-10 23:47:31 +0200 (Sa, 10 Okt 2009) | 1 line

    Show use of range() step argument nicely.
  ........
................
  r75326 | georg.brandl | 2009-10-10 23:57:03 +0200 (Sa, 10 Okt 2009) | 9 lines

  Merged revisions 75325 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r75325 | georg.brandl | 2009-10-10 23:55:11 +0200 (Sa, 10 Okt 2009) | 1 line

    Modernize factorisation demo (mostly augassign.)
  ........
................
  r75328 | georg.brandl | 2009-10-11 00:05:26 +0200 (So, 11 Okt 2009) | 9 lines

  Merged revisions 75327 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r75327 | georg.brandl | 2009-10-11 00:03:43 +0200 (So, 11 Okt 2009) | 1 line

    Style fixes.
  ........
................
  r75330 | georg.brandl | 2009-10-11 00:32:28 +0200 (So, 11 Okt 2009) | 9 lines

  Merged revisions 75329 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r75329 | georg.brandl | 2009-10-11 00:26:45 +0200 (So, 11 Okt 2009) | 1 line

    Modernize all around (dont ask me how useful that script is nowadays...)
  ........
................
  r75338 | georg.brandl | 2009-10-11 10:31:41 +0200 (So, 11 Okt 2009) | 9 lines

  Merged revisions 75337 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r75337 | georg.brandl | 2009-10-11 10:18:44 +0200 (So, 11 Okt 2009) | 1 line

    Update morse script, avoid globals, use iterators.
  ........
................
  r75340 | georg.brandl | 2009-10-11 10:42:09 +0200 (So, 11 Okt 2009) | 9 lines

  Merged revisions 75339 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r75339 | georg.brandl | 2009-10-11 10:39:16 +0200 (So, 11 Okt 2009) | 1 line

    Update markov demo.
  ........
................
  r75341 | georg.brandl | 2009-10-11 10:43:08 +0200 (So, 11 Okt 2009) | 1 line

  Fix README description.
................
  r75343 | georg.brandl | 2009-10-11 10:46:56 +0200 (So, 11 Okt 2009) | 9 lines

  Merged revisions 75342 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r75342 | georg.brandl | 2009-10-11 10:45:03 +0200 (So, 11 Okt 2009) | 1 line

    Remove useless script "mkrcs" and update README.
  ........
................
  r75352 | georg.brandl | 2009-10-11 14:04:10 +0200 (So, 11 Okt 2009) | 9 lines

  Merged revisions 75350 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r75350 | georg.brandl | 2009-10-11 14:00:18 +0200 (So, 11 Okt 2009) | 1 line

    Use getopt in script.py demo.
  ........
................
  r75353 | georg.brandl | 2009-10-11 14:04:40 +0200 (So, 11 Okt 2009) | 9 lines

  Merged revisions 75351 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r75351 | georg.brandl | 2009-10-11 14:03:01 +0200 (So, 11 Okt 2009) | 1 line

    Fix variable.
  ........
................
  r75355 | georg.brandl | 2009-10-11 16:27:51 +0200 (So, 11 Okt 2009) | 9 lines

  Merged revisions 75354 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r75354 | georg.brandl | 2009-10-11 16:23:49 +0200 (So, 11 Okt 2009) | 1 line

    Update lpwatch script.
  ........
................
  r75357 | georg.brandl | 2009-10-11 16:50:57 +0200 (So, 11 Okt 2009) | 9 lines

  Merged revisions 75356 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r75356 | georg.brandl | 2009-10-11 16:49:37 +0200 (So, 11 Okt 2009) | 1 line

    Remove ftpstats script, the daemon whose log files it reads is long gone.
  ........
................
  r75359 | georg.brandl | 2009-10-11 17:56:06 +0200 (So, 11 Okt 2009) | 9 lines

  Merged revisions 75358 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r75358 | georg.brandl | 2009-10-11 17:06:44 +0200 (So, 11 Okt 2009) | 1 line

    Overhaul of Demo/xml.
  ........
................
diff --git a/Demo/scripts/README b/Demo/scripts/README
index d8434e8..097b9b7 100644
--- a/Demo/scripts/README
+++ b/Demo/scripts/README
@@ -5,15 +5,14 @@
 beer.py			Print the classic 'bottles of beer' list.
 eqfix.py		Fix .py files to use the correct equality test operator
 fact.py			Factorize numbers
-find-uname.py		Search for Unicode characters using regexps.
+find-uname.py		Search for Unicode characters using regexps
 from.py			Summarize mailbox
-ftpstats.py		Summarize ftp daemon log file
 lpwatch.py		Watch BSD line printer queues
 makedir.py		Like mkdir -p
 markov.py		Markov chain simulation of words or characters
-mboxconvvert.py		Convert MH or MMDF mailboxes to unix mailbox format
-mkrcs.py		Fix symlinks named RCS into parallel tree
-morse.py		Produce morse code (audible or on AIFF file)
+mboxconvert.py		Convert MH or MMDF mailboxes to unix mailbox format
+morse.py		Produce morse code (as an AIFF file)
+newslist.py		List all newsgroups on a NNTP server as HTML pages
 pi.py			Print all digits of pi -- given enough time and memory
 pp.py			Emulate some Perl command line options
 primes.py		Print prime numbers
diff --git a/Demo/scripts/beer.py b/Demo/scripts/beer.py
index f050572..8135509 100644
--- a/Demo/scripts/beer.py
+++ b/Demo/scripts/beer.py
@@ -1,14 +1,20 @@
 #! /usr/bin/env python
+
 # By GvR, demystified after a version by Fredrik Lundh.
+
 import sys
+
 n = 100
-if sys.argv[1:]: n = int(sys.argv[1])
+if sys.argv[1:]:
+    n = int(sys.argv[1])
+
 def bottle(n):
     if n == 0: return "no more bottles of beer"
     if n == 1: return "one bottle of beer"
     return str(n) + " bottles of beer"
-for i in range(n):
-    print(bottle(n-i), "on the wall,")
-    print(bottle(n-i) + ".")
+
+for i in range(n, 0, -1):
+    print(bottle(i), "on the wall,")
+    print(bottle(i) + ".")
     print("Take one down, pass it around,")
-    print(bottle(n-i-1), "on the wall.")
+    print(bottle(i-1), "on the wall.")
diff --git a/Demo/scripts/fact.py b/Demo/scripts/fact.py
index c76474c..71fcda2 100755
--- a/Demo/scripts/fact.py
+++ b/Demo/scripts/fact.py
@@ -9,39 +9,41 @@
 from math import sqrt
 
 def fact(n):
-    if n < 1: raise ValueError # fact() argument should be >= 1
-    if n == 1: return []    # special case
+    if n < 1:
+        raise ValueError('fact() argument should be >= 1')
+    if n == 1:
+        return []  # special case
     res = []
-    # Treat even factors special, so we can use i = i+2 later
-    while n%2 == 0:
+    # Treat even factors special, so we can use i += 2 later
+    while n % 2 == 0:
         res.append(2)
-        n = n//2
+        n //= 2
     # Try odd numbers up to sqrt(n)
-    limit = sqrt(float(n+1))
+    limit = sqrt(n+1)
     i = 3
     while i <= limit:
-        if n%i == 0:
+        if n % i == 0:
             res.append(i)
-            n = n//i
+            n //= i
             limit = sqrt(n+1)
         else:
-            i = i+2
+            i += 2
     if n != 1:
         res.append(n)
     return res
 
 def main():
     if len(sys.argv) > 1:
-        for arg in sys.argv[1:]:
-            n = eval(arg)
-            print(n, fact(n))
+        source = sys.argv[1:]
     else:
+        source = iter(input, '')
+    for arg in source:
         try:
-            while 1:
-                n = eval(input())
-                print(n, fact(n))
-        except EOFError:
-            pass
+            n = int(arg)
+        except ValueError:
+            print(arg, 'is not an integer')
+        else:
+            print(n, fact(n))
 
 if __name__ == "__main__":
     main()
diff --git a/Demo/scripts/find-uname.py b/Demo/scripts/find-uname.py
index fbf4033..1902423 100644
--- a/Demo/scripts/find-uname.py
+++ b/Demo/scripts/find-uname.py
@@ -21,20 +21,20 @@
 import re
 
 def main(args):
-    unicode_names= []
+    unicode_names = []
     for ix in range(sys.maxunicode+1):
         try:
-            unicode_names.append( (ix, unicodedata.name(chr(ix))) )
+            unicode_names.append((ix, unicodedata.name(chr(ix))))
         except ValueError: # no name for the character
             pass
     for arg in args:
         pat = re.compile(arg, re.I)
-        matches = [(x,y) for (x,y) in unicode_names
-                       if pat.search(y) is not None]
+        matches = [(y,x) for (x,y) in unicode_names
+                   if pat.search(y) is not None]
         if matches:
             print("***", arg, "matches", "***")
-            for (x,y) in matches:
-                print("%s (%d)" % (y,x))
+            for match in matches:
+                print("%s (%d)" % match)
 
 if __name__ == "__main__":
     main(sys.argv[1:])
diff --git a/Demo/scripts/ftpstats.py b/Demo/scripts/ftpstats.py
deleted file mode 100755
index cb0c242..0000000
--- a/Demo/scripts/ftpstats.py
+++ /dev/null
@@ -1,142 +0,0 @@
-#! /usr/bin/env python
-
-# Extract statistics from ftp daemon log.
-
-# Usage:
-# ftpstats [-m maxitems] [-s search] [file]
-# -m maxitems: restrict number of items in "top-N" lists, default 25.
-# -s string:   restrict statistics to lines containing this string.
-# Default file is /usr/adm/ftpd;  a "-" means read standard input.
-
-# The script must be run on the host where the ftp daemon runs.
-# (At CWI this is currently buizerd.)
-
-import os
-import sys
-import re
-import string
-import getopt
-
-pat = '^([a-zA-Z0-9 :]*)!(.*)!(.*)!([<>].*)!([0-9]+)!([0-9]+)$'
-prog = re.compile(pat)
-
-def main():
-    maxitems = 25
-    search = None
-    try:
-        opts, args = getopt.getopt(sys.argv[1:], 'm:s:')
-    except getopt.error as msg:
-        print(msg)
-        print('usage: ftpstats [-m maxitems] [file]')
-        sys.exit(2)
-    for o, a in opts:
-        if o == '-m':
-            maxitems = string.atoi(a)
-        if o == '-s':
-            search = a
-    file = '/usr/adm/ftpd'
-    if args: file = args[0]
-    if file == '-':
-        f = sys.stdin
-    else:
-        try:
-            f = open(file, 'r')
-        except IOError as msg:
-            print(file, ':', msg)
-            sys.exit(1)
-    bydate = {}
-    bytime = {}
-    byfile = {}
-    bydir = {}
-    byhost = {}
-    byuser = {}
-    bytype = {}
-    lineno = 0
-    try:
-        while 1:
-            line = f.readline()
-            if not line: break
-            lineno = lineno + 1
-            if search and string.find(line, search) < 0:
-                continue
-            if prog.match(line) < 0:
-                print('Bad line', lineno, ':', repr(line))
-                continue
-            items = prog.group(1, 2, 3, 4, 5, 6)
-            (logtime, loguser, loghost, logfile, logbytes,
-             logxxx2) = items
-##                      print logtime
-##                      print '-->', loguser
-##                      print '--> -->', loghost
-##                      print '--> --> -->', logfile
-##                      print '--> --> --> -->', logbytes
-##                      print '--> --> --> --> -->', logxxx2
-##                      for i in logtime, loghost, logbytes, logxxx2:
-##                              if '!' in i: print '???', i
-            add(bydate, logtime[-4:] + ' ' + logtime[:6], items)
-            add(bytime, logtime[7:9] + ':00-59', items)
-            direction, logfile = logfile[0], logfile[1:]
-            # The real path probably starts at the last //...
-            while 1:
-                i = string.find(logfile, '//')
-                if i < 0: break
-                logfile = logfile[i+1:]
-            add(byfile, logfile + ' ' + direction, items)
-            logdir = os.path.dirname(logfile)
-##              logdir = os.path.normpath(logdir) + '/.'
-            while 1:
-                add(bydir, logdir + ' ' + direction, items)
-                dirhead = os.path.dirname(logdir)
-                if dirhead == logdir: break
-                logdir = dirhead
-            add(byhost, loghost, items)
-            add(byuser, loguser, items)
-            add(bytype, direction, items)
-    except KeyboardInterrupt:
-        print('Interrupted at line', lineno)
-    show(bytype, 'by transfer direction', maxitems)
-    show(bydir, 'by directory', maxitems)
-    show(byfile, 'by file', maxitems)
-    show(byhost, 'by host', maxitems)
-    show(byuser, 'by user', maxitems)
-    showbar(bydate, 'by date')
-    showbar(bytime, 'by time of day')
-
-def showbar(dict, title):
-    n = len(title)
-    print('='*((70-n)//2), title, '='*((71-n)//2))
-    list = []
-    for key in sorted(dict.keys()):
-        n = len(str(key))
-        list.append((len(dict[key]), key))
-    maxkeylength = 0
-    maxcount = 0
-    for count, key in list:
-        maxkeylength = max(maxkeylength, len(key))
-        maxcount = max(maxcount, count)
-    maxbarlength = 72 - maxkeylength - 7
-    for count, key in list:
-        barlength = int(round(maxbarlength*float(count)/maxcount))
-        bar = '*'*barlength
-        print('%5d %-*s %s' % (count, maxkeylength, key, bar))
-
-def show(dict, title, maxitems):
-    if len(dict) > maxitems:
-        title = title + ' (first %d)'%maxitems
-    n = len(title)
-    print('='*((70-n)//2), title, '='*((71-n)//2))
-    list = []
-    for key in dict.keys():
-        list.append((-len(dict[key]), key))
-    list.sort()
-    for count, key in list[:maxitems]:
-        print('%5d %s' % (-count, key))
-
-def add(dict, key, item):
-    if key in dict:
-        dict[key].append(item)
-    else:
-        dict[key] = [item]
-
-if __name__ == "__main__":
-    main()
diff --git a/Demo/scripts/lpwatch.py b/Demo/scripts/lpwatch.py
index 262e562..90b3ecf 100755
--- a/Demo/scripts/lpwatch.py
+++ b/Demo/scripts/lpwatch.py
@@ -3,10 +3,9 @@
 # Watch line printer queue(s).
 # Intended for BSD 4.3 lpq.
 
-import posix
+import os
 import sys
 import time
-import string
 
 DEF_PRINTER = 'psc'
 DEF_DELAY = 10
@@ -14,94 +13,87 @@
 def main():
     delay = DEF_DELAY # XXX Use getopt() later
     try:
-        thisuser = posix.environ['LOGNAME']
+        thisuser = os.environ['LOGNAME']
     except:
-        thisuser = posix.environ['USER']
+        thisuser = os.environ['USER']
     printers = sys.argv[1:]
     if printers:
         # Strip '-P' from printer names just in case
         # the user specified it...
-        for i in range(len(printers)):
-            if printers[i][:2] == '-P':
-                printers[i] = printers[i][2:]
+        for i, name in enumerate(printers):
+            if name[:2] == '-P':
+                printers[i] = name[2:]
     else:
-        if 'PRINTER' in posix.environ:
-            printers = [posix.environ['PRINTER']]
+        if 'PRINTER' in os.environ:
+            printers = [os.environ['PRINTER']]
         else:
             printers = [DEF_PRINTER]
-    #
-    clearhome = posix.popen('clear', 'r').read()
-    #
-    while 1:
+
+    clearhome = os.popen('clear', 'r').read()
+
+    while True:
         text = clearhome
         for name in printers:
-            text = text + makestatus(name, thisuser) + '\n'
+            text += makestatus(name, thisuser) + '\n'
         print(text)
         time.sleep(delay)
 
 def makestatus(name, thisuser):
-    pipe = posix.popen('lpq -P' + name + ' 2>&1', 'r')
+    pipe = os.popen('lpq -P' + name + ' 2>&1', 'r')
     lines = []
     users = {}
     aheadbytes = 0
     aheadjobs = 0
-    userseen = 0
+    userseen = False
     totalbytes = 0
     totaljobs = 0
-    while 1:
-        line = pipe.readline()
-        if not line: break
-        fields = string.split(line)
+    for line in pipe:
+        fields = line.split()
         n = len(fields)
         if len(fields) >= 6 and fields[n-1] == 'bytes':
-            rank = fields[0]
-            user = fields[1]
-            job = fields[2]
+            rank, user, job = fields[0:3]
             files = fields[3:-2]
-            bytes = eval(fields[n-2])
+            bytes = int(fields[n-2])
             if user == thisuser:
-                userseen = 1
+                userseen = True
             elif not userseen:
-                aheadbytes = aheadbytes + bytes
-                aheadjobs = aheadjobs + 1
-            totalbytes = totalbytes + bytes
-            totaljobs = totaljobs + 1
-            if user in users:
-                ujobs, ubytes = users[user]
-            else:
-                ujobs, ubytes = 0, 0
-            ujobs = ujobs + 1
-            ubytes = ubytes + bytes
+                aheadbytes += bytes
+                aheadjobs += 1
+            totalbytes += bytes
+            totaljobs += 1
+            ujobs, ubytes = users.get(user, (0, 0))
+            ujobs += 1
+            ubytes += bytes
             users[user] = ujobs, ubytes
         else:
             if fields and fields[0] != 'Rank':
-                line = string.strip(line)
+                line = line.strip()
                 if line == 'no entries':
                     line = name + ': idle'
                 elif line[-22:] == ' is ready and printing':
                     line = name
                 lines.append(line)
-    #
+
     if totaljobs:
-        line = '%d K' % ((totalbytes+1023)//1024)
+        line = '%d K' % ((totalbytes+1023) // 1024)
         if totaljobs != len(users):
-            line = line + ' (%d jobs)' % totaljobs
+            line += ' (%d jobs)' % totaljobs
         if len(users) == 1:
-            line = line + ' for %s' % (list(users.keys())[0],)
+            line += ' for %s' % next(iter(users))
         else:
-            line = line + ' for %d users' % len(users)
+            line += ' for %d users' % len(users)
             if userseen:
                 if aheadjobs == 0:
-                    line =  line + ' (%s first)' % thisuser
+                    line += ' (%s first)' % thisuser
                 else:
-                    line = line + ' (%d K before %s)' % (
-                                   (aheadbytes+1023)//1024, thisuser)
+                    line += ' (%d K before %s)' % (
+                        (aheadbytes+1023) // 1024, thisuser)
         lines.append(line)
-    #
+
     sts = pipe.close()
     if sts:
         lines.append('lpq exit status %r' % (sts,))
-    return string.joinfields(lines, ': ')
+    return ': '.join(lines)
 
 if __name__ == "__main__":
     try:
diff --git a/Demo/scripts/markov.py b/Demo/scripts/markov.py
index 6f3482b..990c972 100755
--- a/Demo/scripts/markov.py
+++ b/Demo/scripts/markov.py
@@ -5,11 +5,10 @@
         self.histsize = histsize
         self.choice = choice
         self.trans = {}
+
     def add(self, state, next):
-        if state not in self.trans:
-            self.trans[state] = [next]
-        else:
-            self.trans[state].append(next)
+        self.trans.setdefault(state, []).append(next)
+
     def put(self, seq):
         n = self.histsize
         add = self.add
@@ -17,26 +16,29 @@
         for i in range(len(seq)):
             add(seq[max(0, i-n):i], seq[i:i+1])
         add(seq[len(seq)-n:], None)
+
     def get(self):
         choice = self.choice
         trans = self.trans
         n = self.histsize
         seq = choice(trans[None])
-        while 1:
+        while True:
             subseq = seq[max(0, len(seq)-n):]
             options = trans[subseq]
             next = choice(options)
-            if not next: break
-            seq = seq + next
+            if not next:
+                break
+            seq += next
         return seq
 
+
 def test():
-    import sys, string, random, getopt
+    import sys, random, getopt
     args = sys.argv[1:]
     try:
-        opts, args = getopt.getopt(args, '0123456789cdw')
+        opts, args = getopt.getopt(args, '0123456789cdwq')
     except getopt.error:
-        print('Usage: markov [-#] [-cddqw] [file] ...')
+        print('Usage: %s [-#] [-cddqw] [file] ...' % sys.argv[0])
         print('Options:')
         print('-#: 1-digit history size (default 2)')
         print('-c: characters (default)')
@@ -49,16 +51,19 @@
         print('exactly one space separating words.')
         print('Output consists of paragraphs separated by blank')
         print('lines, where lines are no longer than 72 characters.')
+        sys.exit(2)
     histsize = 2
-    do_words = 0
+    do_words = False
     debug = 1
     for o, a in opts:
-        if '-0' <= o <= '-9': histsize = eval(o[1:])
-        if o == '-c': do_words = 0
-        if o == '-d': debug = debug + 1
+        if '-0' <= o <= '-9': histsize = int(o[1:])
+        if o == '-c': do_words = False
+        if o == '-d': debug += 1
         if o == '-q': debug = 0
-        if o == '-w': do_words = 1
-    if not args: args = ['-']
+        if o == '-w': do_words = True
+    if not args:
+        args = ['-']
+
     m = Markov(histsize, random.choice)
     try:
         for filename in args:
@@ -72,13 +77,15 @@
             if debug: print('processing', filename, '...')
             text = f.read()
             f.close()
-            paralist = string.splitfields(text, '\n\n')
+            paralist = text.split('\n\n')
             for para in paralist:
                 if debug > 1: print('feeding ...')
-                words = string.split(para)
+                words = para.split()
                 if words:
-                    if do_words: data = tuple(words)
-                    else: data = string.joinfields(words, ' ')
+                    if do_words:
+                        data = tuple(words)
+                    else:
+                        data = ' '.join(words)
                     m.put(data)
     except KeyboardInterrupt:
         print('Interrupted -- continue with data read so far')
@@ -86,16 +93,19 @@
         print('No valid input files')
         return
     if debug: print('done.')
+
     if debug > 1:
         for key in m.trans.keys():
             if key is None or len(key) < histsize:
                 print(repr(key), m.trans[key])
         if histsize == 0: print(repr(''), m.trans[''])
         print()
-    while 1:
+    while True:
         data = m.get()
-        if do_words: words = data
-        else: words = string.split(data)
+        if do_words:
+            words = data
+        else:
+            words = data.split()
         n = 0
         limit = 72
         for w in words:
@@ -103,15 +113,9 @@
                 print()
                 n = 0
             print(w, end=' ')
-            n = n + len(w) + 1
+            n += len(w) + 1
         print()
         print()
 
-def tuple(list):
-    if len(list) == 0: return ()
-    if len(list) == 1: return (list[0],)
-    i = len(list)//2
-    return tuple(list[:i]) + tuple(list[i:])
-
 if __name__ == "__main__":
     test()
diff --git a/Demo/scripts/mkrcs.py b/Demo/scripts/mkrcs.py
deleted file mode 100755
index 317647a..0000000
--- a/Demo/scripts/mkrcs.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#! /usr/bin/env python
-
-# A rather specialized script to make sure that a symbolic link named
-# RCS exists pointing to a real RCS directory in a parallel tree
-# referenced as RCStree in an ancestor directory.
-# (I use this because I like my RCS files to reside on a physically
-# different machine).
-
-import os
-
-def main():
-    rcstree = 'RCStree'
-    rcs = 'RCS'
-    if os.path.islink(rcs):
-        print('%r is a symlink to %r' % (rcs, os.readlink(rcs)))
-        return
-    if os.path.isdir(rcs):
-        print('%r is an ordinary directory' % (rcs,))
-        return
-    if os.path.exists(rcs):
-        print('%r is a file?!?!' % (rcs,))
-        return
-    #
-    p = os.getcwd()
-    up = ''
-    down = ''
-    # Invariants:
-    # (1) join(p, down) is the current directory
-    # (2) up is the same directory as p
-    # Ergo:
-    # (3) join(up, down) is the current directory
-    #print 'p =', repr(p)
-    while not os.path.isdir(os.path.join(p, rcstree)):
-        head, tail = os.path.split(p)
-        #print 'head = %r; tail = %r' % (head, tail)
-        if not tail:
-            print('Sorry, no ancestor dir contains %r' % (rcstree,))
-            return
-        p = head
-        up = os.path.join(os.pardir, up)
-        down = os.path.join(tail, down)
-        #print 'p = %r; up = %r; down = %r' % (p, up, down)
-    there = os.path.join(up, rcstree)
-    there = os.path.join(there, down)
-    there = os.path.join(there, rcs)
-    if os.path.isdir(there):
-        print('%r already exists' % (there, ))
-    else:
-        print('making %r' % (there,))
-        makedirs(there)
-    print('making symlink %r -> %r' % (rcs, there))
-    os.symlink(there, rcs)
-
-def makedirs(p):
-    if not os.path.isdir(p):
-        head, tail = os.path.split(p)
-        makedirs(head)
-        os.mkdir(p, 0o777)
-
-if __name__ == "__main__":
-    main()
diff --git a/Demo/scripts/morse.py b/Demo/scripts/morse.py
new file mode 100755
index 0000000..5aacaa1
--- /dev/null
+++ b/Demo/scripts/morse.py
@@ -0,0 +1,128 @@
+#! /usr/bin/env python
+
+# DAH should be three DOTs.
+# Space between DOTs and DAHs should be one DOT.
+# Space between two letters should be one DAH.
+# Space between two words should be DOT DAH DAH.
+
+import sys, math, aifc
+from contextlib import closing
+
+DOT = 30
+DAH = 3 * DOT
+OCTAVE = 2                              # 1 == 441 Hz, 2 == 882 Hz, ...
+
+morsetab = {
+        'A': '.-',              'a': '.-',
+        'B': '-...',            'b': '-...',
+        'C': '-.-.',            'c': '-.-.',
+        'D': '-..',             'd': '-..',
+        'E': '.',               'e': '.',
+        'F': '..-.',            'f': '..-.',
+        'G': '--.',             'g': '--.',
+        'H': '....',            'h': '....',
+        'I': '..',              'i': '..',
+        'J': '.---',            'j': '.---',
+        'K': '-.-',             'k': '-.-',
+        'L': '.-..',            'l': '.-..',
+        'M': '--',              'm': '--',
+        'N': '-.',              'n': '-.',
+        'O': '---',             'o': '---',
+        'P': '.--.',            'p': '.--.',
+        'Q': '--.-',            'q': '--.-',
+        'R': '.-.',             'r': '.-.',
+        'S': '...',             's': '...',
+        'T': '-',               't': '-',
+        'U': '..-',             'u': '..-',
+        'V': '...-',            'v': '...-',
+        'W': '.--',             'w': '.--',
+        'X': '-..-',            'x': '-..-',
+        'Y': '-.--',            'y': '-.--',
+        'Z': '--..',            'z': '--..',
+        '0': '-----',           ',': '--..--',
+        '1': '.----',           '.': '.-.-.-',
+        '2': '..---',           '?': '..--..',
+        '3': '...--',           ';': '-.-.-.',
+        '4': '....-',           ':': '---...',
+        '5': '.....',           "'": '.----.',
+        '6': '-....',           '-': '-....-',
+        '7': '--...',           '/': '-..-.',
+        '8': '---..',           '(': '-.--.-',
+        '9': '----.',           ')': '-.--.-',
+        ' ': ' ',               '_': '..--.-',
+}
+
+nowave = b'\0' * 200
+
+# If we play at 44.1 kHz (which we do), then if we produce one sine
+# wave in 100 samples, we get a tone of 441 Hz.  If we produce two
+# sine waves in these 100 samples, we get a tone of 882 Hz.  882 Hz
+# appears to be a nice one for playing morse code.
+def mkwave(octave):
+    sinewave = bytearray()
+    for i in range(100):
+        val = int(math.sin(math.pi * i * octave / 50.0) * 30000)
+        sinewave.extend([(val >> 8) & 255, val & 255])
+    return bytes(sinewave)
+
+defaultwave = mkwave(OCTAVE)
+
+def main():
+    import getopt
+    try:
+        opts, args = getopt.getopt(sys.argv[1:], 'o:p:')
+    except getopt.error:
+        sys.stderr.write('Usage ' + sys.argv[0] +
+                         ' [ -o outfile ] [ -p octave ] [ words ] ...\n')
+        sys.exit(1)
+    wave = defaultwave
+    outfile = 'morse.aifc'
+    for o, a in opts:
+        if o == '-o':
+            outfile = a
+        if o == '-p':
+            wave = mkwave(int(a))
+    with closing(aifc.open(outfile, 'w')) as fp:
+        fp.setframerate(44100)
+        fp.setsampwidth(2)
+        fp.setnchannels(1)
+        if args:
+            source = [' '.join(args)]
+        else:
+            source = iter(sys.stdin.readline, '')
+        for line in source:
+            mline = morse(line)
+            play(mline, fp, wave)
+
+# Convert a string to morse code with \001 between the characters in
+# the string.
+def morse(line):
+    res = ''
+    for c in line:
+        try:
+            res += morsetab[c] + '\001'
+        except KeyError:
+            pass
+    return res
+
+# Play a line of morse code.
+def play(line, fp, wave):
+    for c in line:
+        if c == '.':
+            sine(fp, DOT, wave)
+        elif c == '-':
+            sine(fp, DAH, wave)
+        else:                   # space
+            pause(fp, DAH + DOT)
+        pause(fp, DOT)
+
+def sine(fp, length, wave):
+    for i in range(length):
+        fp.writeframesraw(wave)
+
+def pause(fp, length):
+    for i in range(length):
+        fp.writeframesraw(nowave)
+
+if __name__ == '__main__':
+    main()
diff --git a/Demo/scripts/newslist.py b/Demo/scripts/newslist.py
index 02b4b7c..9cea1b4 100755
--- a/Demo/scripts/newslist.py
+++ b/Demo/scripts/newslist.py
@@ -32,22 +32,22 @@
 # fraser@europarc.xerox.com                     qs101@cl.cam.ac.uk
 #                                                                     #
 #######################################################################
-import sys,nntplib, string, marshal, time, os, posix, string
+import sys, nntplib, marshal, time, os
 
 #######################################################################
 # Check these variables before running!                               #
 
 # Top directory.
 # Filenames which don't start with / are taken as being relative to this.
-topdir='/anfs/qsbigdisc/web/html/newspage'
+topdir = os.path.expanduser('~/newspage')
 
 # The name of your NNTP host
 # eg.
 #    newshost = 'nntp-serv.cl.cam.ac.uk'
 # or use following to get the name from the NNTPSERVER environment
 # variable:
-#    newshost = posix.environ['NNTPSERVER']
-newshost = 'nntp-serv.cl.cam.ac.uk'
+#    newshost = os.environ['NNTPSERVER']
+newshost = 'news.example.com'
 
 # The filename for a local cache of the newsgroup list
 treefile = 'grouptree'
@@ -81,7 +81,7 @@
 # pagelinkicon can contain html to put an icon after links to
 # further pages. This helps to make important links stand out.
 # Set to '' if not wanted, or '...' is quite a good one.
-pagelinkicon='... <img src="http://pelican.cl.cam.ac.uk/icons/page.xbm"> '
+pagelinkicon = '... <img src="http://pelican.cl.cam.ac.uk/icons/page.xbm"> '
 
 # ---------------------------------------------------------------------
 # Less important personal preferences:
@@ -106,7 +106,7 @@
 from stat import *
 
 rcsrev = '$Revision$'
-rcsrev = string.join([s for s in string.split(rcsrev) if '$' not in s])
+rcsrev = ' '.join([s for s in rcsrev.split() if '$' not in s])
 desc = {}
 
 # Make (possibly) relative filenames into absolute ones
@@ -120,7 +120,7 @@
 def addtotree(tree, groups):
     print('Updating tree...')
     for i in groups:
-        parts = string.splitfields(i,'.')
+        parts = i.split('.')
         makeleaf(tree, parts)
 
 # Makeleaf makes a leaf and the branch leading to it if necessary
@@ -141,34 +141,38 @@
 # to those groups beginning with <root>.
 
 def createpage(root, tree, p):
-    filename = os.path.join(pagedir,root+'.html')
+    filename = os.path.join(pagedir, root+'.html')
     if root == rootpage:
         detail = ''
     else:
         detail = ' under ' + root
-    f = open(filename,'w')
-    # f.write('Content-Type: text/html\n')
-    f.write('<TITLE>Newsgroups available' + detail + '</TITLE>\n')
-    f.write('<H1>Newsgroups available' + detail +'</H1>\n')
-    f.write('<A HREF="'+httppref+rootpage+'.html">Back to top level</A><P>\n')
-    printtree(f,tree,0,p)
-    f.write('<I>This page automatically created by \'newslist\' v. '+rcsrev+'.')
-    f.write(time.ctime(time.time()) + '</I><P>')
-    f.close()
+    with open(filename, 'w') as f:
+        # f.write('Content-Type: text/html\n')
+        f.write('<html>\n<head>\n')
+        f.write('<title>Newsgroups available%s</title>\n' % detail)
+        f.write('</head>\n<body>\n')
+        f.write('<h1>Newsgroups available%s</h1>\n' % detail)
+        f.write('<a href="%s%s.html">Back to top level</a><p>\n' %
+                (httppref, rootpage))
+        printtree(f, tree, 0, p)
+        f.write('\n<p>')
+        f.write("<i>This page automatically created by 'newslist' v. %s." %
+                rcsrev)
+        f.write(time.ctime(time.time()) + '</i>\n')
+        f.write('</body>\n</html>\n')
 
 # Printtree prints the groups as a bulleted list.  Groups with
 # more than <sublistsize> subgroups will be put on a separate page.
 # Other sets of subgroups are just indented.
 
 def printtree(f, tree, indent, p):
-    global desc
     l = len(tree)
 
-    if l > sublistsize and indent>0:
+    if l > sublistsize and indent > 0:
         # Create a new page and a link to it
-        f.write('<LI><B><A HREF="'+httppref+p[1:]+'.html">')
-        f.write(p[1:]+'.*')
-        f.write('</A></B>'+pagelinkicon+'\n')
+        f.write('<li><b><a href="%s%s.html">' % (httppref, p[1:]))
+        f.write(p[1:] + '.*')
+        f.write('</a></b>%s\n' % pagelinkicon)
         createpage(p[1:], tree, p)
         return
 
@@ -177,67 +181,64 @@
     if l > 1:
         if indent > 0:
             # Create a sub-list
-            f.write('<LI>'+p[1:]+'\n<UL>')
+            f.write('<li>%s\n<ul>' % p[1:])
         else:
             # Create a main list
-            f.write('<UL>')
+            f.write('<ul>')
         indent = indent + 1
 
     for i in kl:
         if i == '.':
             # Output a newsgroup
-            f.write('<LI><A HREF="news:' + p[1:] + '">'+ p[1:] + '</A> ')
+            f.write('<li><a href="news:%s">%s</a> ' % (p[1:], p[1:]))
             if p[1:] in desc:
-                f.write('     <I>'+desc[p[1:]]+'</I>\n')
+                f.write('     <i>%s</i>\n' % desc[p[1:]])
             else:
                 f.write('\n')
         else:
             # Output a hierarchy
-            printtree(f,tree[i], indent, p+'.'+i)
+            printtree(f, tree[i], indent, p+'.'+i)
 
     if l > 1:
-        f.write('\n</UL>')
+        f.write('\n</ul>')
 
 # Reading descriptions file ---------------------------------------
 
-# This returns an array mapping group name to its description
+# This returns a dict mapping group name to its description
 
 def readdesc(descfile):
     global desc
-
     desc = {}
 
     if descfile == '':
         return
 
     try:
-        d = open(descfile, 'r')
-        print('Reading descriptions...')
-    except (IOError):
+        with open(descfile, 'r') as d:
+            print('Reading descriptions...')
+            for l in d:
+                bits = l.split()
+                try:
+                    grp = bits[0]
+                    dsc = ' '.join(bits[1:])
+                    if len(dsc) > 1:
+                        desc[grp] = dsc
+                except IndexError:
+                    pass
+    except IOError:
         print('Failed to open description file ' + descfile)
         return
-    l = d.readline()
-    while l != '':
-        bits = string.split(l)
-        try:
-            grp = bits[0]
-            dsc = string.join(bits[1:])
-            if len(dsc)>1:
-                desc[grp] = dsc
-        except (IndexError):
-            pass
-        l = d.readline()
 
 # Check that ouput directory exists, ------------------------------
 # and offer to create it if not
 
 def checkopdir(pagedir):
     if not os.path.isdir(pagedir):
-        print('Directory '+pagedir+' does not exist.')
+        print('Directory %s does not exist.' % pagedir)
         print('Shall I create it for you? (y/n)')
         if sys.stdin.readline()[0] == 'y':
             try:
-                os.mkdir(pagedir,0o777)
+                os.mkdir(pagedir, 0o777)
             except:
                 print('Sorry - failed!')
                 sys.exit(1)
@@ -257,23 +258,21 @@
         print('If this is the first time you have run newslist, then')
         print('use the -a option to create it.')
         sys.exit(1)
-    treedate = '%02d%02d%02d' % (treetime[0] % 100 ,treetime[1], treetime[2])
+    treedate = '%02d%02d%02d' % (treetime[0] % 100, treetime[1], treetime[2])
     try:
-        dump = open(treefile,'r')
-        tree = marshal.load(dump)
-        dump.close()
-    except (IOError):
+        with open(treefile, 'rb') as dump:
+            tree = marshal.load(dump)
+    except IOError:
         print('Cannot open local group list ' + treefile)
     return (tree, treedate)
 
 def writelocallist(treefile, tree):
     try:
-        dump = open(treefile,'w')
-        groups = marshal.dump(tree,dump)
-        dump.close()
-        print('Saved list to '+treefile+'\n')
+        with open(treefile, 'wb') as dump:
+            groups = marshal.dump(tree, dump)
+        print('Saved list to %s\n' % treefile)
     except:
-        print('Sorry - failed to write to local group cache '+treefile)
+        print('Sorry - failed to write to local group cache', treefile)
         print('Does it (or its directory) have the correct permissions?')
         sys.exit(1)
 
@@ -281,18 +280,18 @@
 
 def getallgroups(server):
     print('Getting list of all groups...')
-    treedate='010101'
+    treedate = '010101'
     info = server.list()[1]
     groups = []
     print('Processing...')
     if skipempty:
         print('\nIgnoring following empty groups:')
     for i in info:
-        grpname = string.split(i[0])[0]
-        if skipempty and string.atoi(i[1]) < string.atoi(i[2]):
-            print(grpname+' ', end=' ')
+        grpname = i[0].split()[0]
+        if skipempty and int(i[1]) < int(i[2]):
+            print(grpname.decode() + ' ', end=' ')
         else:
-            groups.append(grpname)
+            groups.append(grpname.decode())
     print('\n')
     if skipempty:
         print('(End of empty groups)')
@@ -301,42 +300,39 @@
 # Return list of new groups on server -----------------------------
 
 def getnewgroups(server, treedate):
-    print('Getting list of new groups since start of '+treedate+'...', end=' ')
-    info = server.newgroups(treedate,'000001')[1]
+    print('Getting list of new groups since start of %s...' % treedate, end=' ')
+    info = server.newgroups(treedate, '000001')[1]
     print('got %d.' % len(info))
     print('Processing...', end=' ')
     groups = []
     for i in info:
-        grpname = string.split(i)[0]
-        groups.append(grpname)
+        grpname = i.split()[0]
+        groups.append(grpname.decode())
     print('Done')
     return groups
 
 # Now the main program --------------------------------------------
 
 def main():
-    global desc
-
-    tree={}
+    tree = {}
 
     # Check that the output directory exists
     checkopdir(pagedir)
 
     try:
-        print('Connecting to '+newshost+'...')
+        print('Connecting to %s...' % newshost)
         if sys.version[0] == '0':
             s = NNTP.init(newshost)
         else:
             s = NNTP(newshost)
-        connected = 1
+        connected = True
     except (nntplib.error_temp, nntplib.error_perm) as x:
         print('Error connecting to host:', x)
         print('I\'ll try to use just the local list.')
-        connected = 0
+        connected = False
 
     # If -a is specified, read the full list of groups from server
     if connected and len(sys.argv) > 1 and sys.argv[1] == '-a':
-
         groups = getallgroups(s)
 
     # Otherwise just read the local file and then add
diff --git a/Demo/scripts/pi.py b/Demo/scripts/pi.py
index 19733cb..0740cd0 100755
--- a/Demo/scripts/pi.py
+++ b/Demo/scripts/pi.py
@@ -12,7 +12,7 @@
 
 def main():
     k, a, b, a1, b1 = 2, 4, 1, 12, 4
-    while 1:
+    while True:
         # Next approximation
         p, q, k = k*k, 2*k+1, k+1
         a, b, a1, b1 = a1, b1, p*a+q*a1, p*b+q*b1
@@ -25,7 +25,6 @@
 
 def output(d):
     # Use write() to avoid spaces between the digits
-    # Use str() to avoid the 'L'
     sys.stdout.write(str(d))
     # Flush so the output is seen immediately
     sys.stdout.flush()
diff --git a/Demo/scripts/pp.py b/Demo/scripts/pp.py
index 9010b7a..2c948f7 100755
--- a/Demo/scripts/pp.py
+++ b/Demo/scripts/pp.py
@@ -22,7 +22,6 @@
 # - except for -n/-p, run directly from the file if at all possible
 
 import sys
-import string
 import getopt
 
 FS = ''
@@ -36,7 +35,7 @@
 try:
     optlist, ARGS = getopt.getopt(sys.argv[1:], 'acde:F:np')
 except getopt.error as msg:
-    sys.stderr.write(sys.argv[0] + ': ' + msg + '\n')
+    sys.stderr.write('%s: %s\n' % (sys.argv[0], msg))
     sys.exit(2)
 
 for option, optarg in optlist:
@@ -47,7 +46,7 @@
     elif option == '-d':
         DFLAG = 1
     elif option == '-e':
-        for line in string.splitfields(optarg, '\n'):
+        for line in optarg.split('\n'):
             SCRIPT.append(line)
     elif option == '-F':
         FS = optarg
@@ -81,31 +80,31 @@
 elif NFLAG:
     # Note that it is on purpose that AFLAG and PFLAG are
     # tested dynamically each time through the loop
-    prologue = [ \
-            'LINECOUNT = 0', \
-            'for FILE in ARGS:', \
-            '   \tif FILE == \'-\':', \
-            '   \t   \tFP = sys.stdin', \
-            '   \telse:', \
-            '   \t   \tFP = open(FILE, \'r\')', \
-            '   \tLINENO = 0', \
-            '   \twhile 1:', \
-            '   \t   \tLINE = FP.readline()', \
-            '   \t   \tif not LINE: break', \
-            '   \t   \tLINENO = LINENO + 1', \
-            '   \t   \tLINECOUNT = LINECOUNT + 1', \
-            '   \t   \tL = LINE[:-1]', \
-            '   \t   \taflag = AFLAG', \
-            '   \t   \tif aflag:', \
-            '   \t   \t   \tif FS: F = string.splitfields(L, FS)', \
-            '   \t   \t   \telse: F = string.split(L)' \
+    prologue = [
+            'LINECOUNT = 0',
+            'for FILE in ARGS:',
+            '   \tif FILE == \'-\':',
+            '   \t   \tFP = sys.stdin',
+            '   \telse:',
+            '   \t   \tFP = open(FILE, \'r\')',
+            '   \tLINENO = 0',
+            '   \twhile 1:',
+            '   \t   \tLINE = FP.readline()',
+            '   \t   \tif not LINE: break',
+            '   \t   \tLINENO = LINENO + 1',
+            '   \t   \tLINECOUNT = LINECOUNT + 1',
+            '   \t   \tL = LINE[:-1]',
+            '   \t   \taflag = AFLAG',
+            '   \t   \tif aflag:',
+            '   \t   \t   \tif FS: F = L.split(FS)',
+            '   \t   \t   \telse: F = L.split()'
             ]
-    epilogue = [ \
-            '   \t   \tif not PFLAG: continue', \
-            '   \t   \tif aflag:', \
-            '   \t   \t   \tif FS: print string.joinfields(F, FS)', \
-            '   \t   \t   \telse: print string.join(F)', \
-            '   \t   \telse: print L', \
+    epilogue = [
+            '   \t   \tif not PFLAG: continue',
+            '   \t   \tif aflag:',
+            '   \t   \t   \tif FS: print(FS.join(F))',
+            '   \t   \t   \telse: print(\' \'.join(F))',
+            '   \t   \telse: print(L)',
             ]
 else:
     prologue = ['if 1:']
@@ -114,18 +113,13 @@
 # Note that we indent using tabs only, so that any indentation style
 # used in 'command' will come out right after re-indentation.
 
-program = string.joinfields(prologue, '\n') + '\n'
+program = '\n'.join(prologue) + '\n'
 for line in SCRIPT:
-    program = program + ('   \t   \t' + line + '\n')
-program = program + (string.joinfields(epilogue, '\n') + '\n')
+    program += '   \t   \t' + line + '\n'
+program += '\n'.join(epilogue) + '\n'
 
-import tempfile
-fp = tempfile.NamedTemporaryFile()
-fp.write(program)
-fp.flush()
-script = open(tfn).read()
 if DFLAG:
     import pdb
-    pdb.run(script)
+    pdb.run(program)
 else:
-    exec(script)
+    exec(program)
diff --git a/Demo/scripts/queens.py b/Demo/scripts/queens.py
index 820c9ad..726433c 100755
--- a/Demo/scripts/queens.py
+++ b/Demo/scripts/queens.py
@@ -19,8 +19,8 @@
 
     def reset(self):
         n = self.n
-        self.y = [None]*n               # Where is the queen in column x
-        self.row = [0]*n                # Is row[y] safe?
+        self.y = [None] * n             # Where is the queen in column x
+        self.row = [0] * n              # Is row[y] safe?
         self.up = [0] * (2*n-1)         # Is upward diagonal[x-y] safe?
         self.down = [0] * (2*n-1)       # Is downward diagonal[x+y] safe?
         self.nfound = 0                 # Instrumentation
@@ -50,7 +50,7 @@
         self.up[x-y] = 0
         self.down[x+y] = 0
 
-    silent = 0                          # If set, count solutions only
+    silent = 0                          # If true, count solutions only
 
     def display(self):
         self.nfound = self.nfound + 1
diff --git a/Demo/scripts/script.py b/Demo/scripts/script.py
index 174c13e..b490b17 100755
--- a/Demo/scripts/script.py
+++ b/Demo/scripts/script.py
@@ -1,4 +1,5 @@
 #! /usr/bin/env python
+
 # script.py -- Make typescript of terminal session.
 # Usage:
 #       -a      Append to typescript.
@@ -6,28 +7,36 @@
 # Author: Steen Lumholt.
 
 
-import os, time, sys
+import os, time, sys, getopt
 import pty
 
 def read(fd):
     data = os.read(fd, 1024)
-    file.write(data)
+    script.write(data)
     return data
 
 shell = 'sh'
 filename = 'typescript'
-mode = 'w'
+mode = 'wb'
 if 'SHELL' in os.environ:
     shell = os.environ['SHELL']
-if '-a' in sys.argv:
-    mode = 'a'
-if '-p' in sys.argv:
-    shell = 'python'
 
-file = open(filename, mode)
+try:
+    opts, args = getopt.getopt(sys.argv[1:], 'ap')
+except getopt.error as msg:
+    print('%s: %s' % (sys.argv[0], msg))
+    sys.exit(2)
+
+for o, a in opts:
+    if o == '-a':
+        mode = 'ab'
+    elif o == '-p':
+        shell = 'python'
+
+script = open(filename, mode)
 
 sys.stdout.write('Script started, file is %s\n' % filename)
-file.write('Script started on %s\n' % time.ctime(time.time()))
+script.write(('Script started on %s\n' % time.ctime(time.time())).encode())
 pty.spawn(shell, read)
-file.write('Script done on %s\n' % time.ctime(time.time()))
+script.write(('Script done on %s\n' % time.ctime(time.time())).encode())
 sys.stdout.write('Script done, file is %s\n' % filename)
diff --git a/Demo/scripts/unbirthday.py b/Demo/scripts/unbirthday.py
index 991537d..af58f8f 100755
--- a/Demo/scripts/unbirthday.py
+++ b/Demo/scripts/unbirthday.py
@@ -9,35 +9,27 @@
 import time
 import calendar
 
-def raw_input(prompt):
-    sys.stdout.write(prompt)
-    sys.stdout.flush()
-    return sys.stdin.readline()
-
 def main():
-    # Note that the range checks below also check for bad types,
-    # e.g. 3.14 or ().  However syntactically invalid replies
-    # will raise an exception.
     if sys.argv[1:]:
         year = int(sys.argv[1])
     else:
         year = int(input('In which year were you born? '))
-    if 0<=year<100:
+    if 0 <= year < 100:
         print("I'll assume that by", year, end=' ')
         year = year + 1900
         print('you mean', year, 'and not the early Christian era')
-    elif not (1850<=year<=2002):
+    elif not (1850 <= year <= time.localtime()[0]):
         print("It's hard to believe you were born in", year)
         return
-    #
+
     if sys.argv[2:]:
         month = int(sys.argv[2])
     else:
         month = int(input('And in which month? (1-12) '))
-    if not (1<=month<=12):
+    if not (1 <= month <= 12):
         print('There is no month numbered', month)
         return
-    #
+
     if sys.argv[3:]:
         day = int(sys.argv[3])
     else:
@@ -46,36 +38,36 @@
         maxday = 29
     else:
         maxday = calendar.mdays[month]
-    if not (1<=day<=maxday):
+    if not (1 <= day <= maxday):
         print('There are no', day, 'days in that month!')
         return
-    #
+
     bdaytuple = (year, month, day)
     bdaydate = mkdate(bdaytuple)
     print('You were born on', format(bdaytuple))
-    #
+
     todaytuple = time.localtime()[:3]
     todaydate = mkdate(todaytuple)
     print('Today is', format(todaytuple))
-    #
+
     if bdaytuple > todaytuple:
         print('You are a time traveler.  Go back to the future!')
         return
-    #
+
     if bdaytuple == todaytuple:
         print('You were born today.  Have a nice life!')
         return
-    #
+
     days = todaydate - bdaydate
     print('You have lived', days, 'days')
-    #
+
     age = 0
     for y in range(year, todaytuple[0] + 1):
         if bdaytuple < (y, month, day) <= todaytuple:
             age = age + 1
-    #
+
     print('You are', age, 'years old')
-    #
+
     if todaytuple[1:] == bdaytuple[1:]:
         print('Congratulations!  Today is your', nth(age), 'birthday')
         print('Yesterday was your', end=' ')
@@ -83,8 +75,8 @@
         print('Today is your', end=' ')
     print(nth(days - age), 'unbirthday')
 
-def format(xxx_todo_changeme):
-    (year, month, day) = xxx_todo_changeme
+def format(date):
+    (year, month, day) = date
     return '%d %s %d' % (day, calendar.month_name[month], year)
 
 def nth(n):
@@ -93,12 +85,12 @@
     if n == 3: return '3rd'
     return '%dth' % n
 
-def mkdate(xxx_todo_changeme1):
-    # Januari 1st, in 0 A.D. is arbitrarily defined to be day 1,
+def mkdate(date):
+    # January 1st, in 0 A.D. is arbitrarily defined to be day 1,
     # even though that day never actually existed and the calendar
     # was different then...
-    (year, month, day) = xxx_todo_changeme1
-    days = year*365                 # years, roughly
+    (year, month, day) = date
+    days = year*365                  # years, roughly
     days = days + (year+3)//4        # plus leap years, roughly
     days = days - (year+99)//100     # minus non-leap years every century
     days = days + (year+399)//400    # plus leap years every 4 centirues