diff options
-rw-r--r-- | Demo/scripts/README | 9 | ||||
-rw-r--r-- | Demo/scripts/beer.py | 16 | ||||
-rwxr-xr-x | Demo/scripts/fact.py | 36 | ||||
-rw-r--r-- | Demo/scripts/find-uname.py | 12 | ||||
-rwxr-xr-x | Demo/scripts/ftpstats.py | 142 | ||||
-rwxr-xr-x | Demo/scripts/lpwatch.py | 84 | ||||
-rwxr-xr-x | Demo/scripts/markov.py | 64 | ||||
-rwxr-xr-x | Demo/scripts/mkrcs.py | 61 | ||||
-rwxr-xr-x | Demo/scripts/morse.py | 128 | ||||
-rwxr-xr-x | Demo/scripts/newslist.py | 140 | ||||
-rwxr-xr-x | Demo/scripts/pi.py | 3 | ||||
-rwxr-xr-x | Demo/scripts/pp.py | 68 | ||||
-rwxr-xr-x | Demo/scripts/queens.py | 6 | ||||
-rwxr-xr-x | Demo/scripts/script.py | 29 | ||||
-rwxr-xr-x | Demo/scripts/unbirthday.py | 48 | ||||
-rw-r--r-- | Demo/xml/elem_count.py | 26 | ||||
-rw-r--r-- | Demo/xml/roundtrip.py | 11 | ||||
-rw-r--r-- | Demo/xml/rss2html.py | 52 | ||||
-rw-r--r-- | Doc/library/configparser.rst | 15 | ||||
-rw-r--r-- | Doc/library/copy.rst | 22 | ||||
-rw-r--r-- | Doc/library/marshal.rst | 14 | ||||
-rw-r--r-- | Doc/library/urllib.parse.rst | 3 | ||||
-rw-r--r-- | Doc/reference/datamodel.rst | 2 | ||||
-rwxr-xr-x | Lib/pdb.py | 4 | ||||
-rwxr-xr-x | Lib/pydoc.py | 12 | ||||
-rw-r--r-- | Lib/test/test_funcattrs.py | 125 | ||||
-rw-r--r-- | Lib/test/test_pdb.py | 88 |
27 files changed, 624 insertions, 596 deletions
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 @@ See also the Tools/scripts directory! 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 @@ import sys 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 sys 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_DELAY = 10 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 @@ class Markov: 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 @@ class Markov: 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 @@ def test(): 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 @@ def test(): 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 @@ def test(): 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 @@ def test(): 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 @@ skipempty = 1 # 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 nntplib import NNTP 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 @@ page = os.path.join(topdir,pagedir) 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 @@ def makeleaf(tree,path): # 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 @@ def printtree(f, tree, indent, p): 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 @@ def readlocallist(treefile): 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 writelocallist(treefile, tree): 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 @@ def getallgroups(server): # 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 @@ import sys 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 main(): 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 @@ PFLAG = 0 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 @@ for option, optarg in optlist: 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 @@ if CFLAG: 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 @@ else: # 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 @@ class Queens: 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 @@ class Queens: 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 sys 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 @@ def main(): 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 @@ def main(): 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 @@ def nth(n): 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 diff --git a/Demo/xml/elem_count.py b/Demo/xml/elem_count.py index e083e64..99d6ca9 100644 --- a/Demo/xml/elem_count.py +++ b/Demo/xml/elem_count.py @@ -1,4 +1,10 @@ +""" +A simple demo that reads in an XML document and displays the number of +elements and attributes as well as a tally of elements and attributes by name. +""" + import sys +from collections import defaultdict from xml.sax import make_parser, handler @@ -7,16 +13,16 @@ class FancyCounter(handler.ContentHandler): def __init__(self): self._elems = 0 self._attrs = 0 - self._elem_types = {} - self._attr_types = {} + self._elem_types = defaultdict(int) + self._attr_types = defaultdict(int) def startElement(self, name, attrs): - self._elems = self._elems + 1 - self._attrs = self._attrs + len(attrs) - self._elem_types[name] = self._elem_types.get(name, 0) + 1 + self._elems += 1 + self._attrs += len(attrs) + self._elem_types[name] += 1 for name in attrs.keys(): - self._attr_types[name] = self._attr_types.get(name, 0) + 1 + self._attr_types[name] += 1 def endDocument(self): print("There were", self._elems, "elements.") @@ -30,7 +36,7 @@ class FancyCounter(handler.ContentHandler): for pair in self._attr_types.items(): print("%20s %d" % pair) - -parser = make_parser() -parser.setContentHandler(FancyCounter()) -parser.parse(sys.argv[1]) +if __name__ == '__main__': + parser = make_parser() + parser.setContentHandler(FancyCounter()) + parser.parse(sys.argv[1]) diff --git a/Demo/xml/roundtrip.py b/Demo/xml/roundtrip.py index 8d7d437..801c009 100644 --- a/Demo/xml/roundtrip.py +++ b/Demo/xml/roundtrip.py @@ -3,7 +3,7 @@ A simple demo that reads in an XML document and spits out an equivalent, but not necessarily identical, document. """ -import sys, string +import sys from xml.sax import saxutils, handler, make_parser @@ -11,7 +11,7 @@ from xml.sax import saxutils, handler, make_parser class ContentGenerator(handler.ContentHandler): - def __init__(self, out = sys.stdout): + def __init__(self, out=sys.stdout): handler.ContentHandler.__init__(self) self._out = out @@ -40,6 +40,7 @@ class ContentGenerator(handler.ContentHandler): # --- The main program -parser = make_parser() -parser.setContentHandler(ContentGenerator()) -parser.parse(sys.argv[1]) +if __name__ == '__main__': + parser = make_parser() + parser.setContentHandler(ContentGenerator()) + parser.parse(sys.argv[1]) diff --git a/Demo/xml/rss2html.py b/Demo/xml/rss2html.py index 15c9891..49cd154 100644 --- a/Demo/xml/rss2html.py +++ b/Demo/xml/rss2html.py @@ -1,45 +1,50 @@ +""" +A demo that reads in an RSS XML document and emits an HTML file containing +a list of the individual items in the feed. +""" + import sys +import codecs from xml.sax import make_parser, handler # --- Templates -top = \ -""" +top = """\ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> -<HTML> -<HEAD> - <TITLE>%s</TITLE> -</HEAD> - -<BODY> -<H1>%s</H1> +<html> +<head> + <title>%s</title> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> +</head> + +<body> +<h1>%s</h1> """ -bottom = \ -""" +bottom = """ </ul> -<HR> -<ADDRESS> -Converted to HTML by sax_rss2html.py. -</ADDRESS> +<hr> +<address> +Converted to HTML by rss2html.py. +</address> -</BODY> -</HTML> +</body> +</html> """ # --- The ContentHandler class RSSHandler(handler.ContentHandler): - def __init__(self, out = sys.stdout): + def __init__(self, out=sys.stdout): handler.ContentHandler.__init__(self) self._out = out self._text = "" self._parent = None - self._list_started = 0 + self._list_started = False self._title = None self._link = None self._descr = "" @@ -69,7 +74,7 @@ class RSSHandler(handler.ContentHandler): elif name == "item": if not self._list_started: self._out.write("<ul>\n") - self._list_started = 1 + self._list_started = True self._out.write(' <li><a href="%s">%s</a> %s\n' % (self._link, self._title, self._descr)) @@ -86,6 +91,7 @@ class RSSHandler(handler.ContentHandler): # --- Main program -parser = make_parser() -parser.setContentHandler(RSSHandler()) -parser.parse(sys.argv[1]) +if __name__ == '__main__': + parser = make_parser() + parser.setContentHandler(RSSHandler()) + parser.parse(sys.argv[1]) diff --git a/Doc/library/configparser.rst b/Doc/library/configparser.rst index a54bc19..a5d3a77 100644 --- a/Doc/library/configparser.rst +++ b/Doc/library/configparser.rst @@ -231,9 +231,11 @@ RawConfigParser Objects .. method:: RawConfigParser.readfp(fp, filename=None) Read and parse configuration data from the file or file-like object in *fp* - (only the :meth:`readline` method is used). If *filename* is omitted and *fp* - has a :attr:`name` attribute, that is used for *filename*; the default is - ``<???>``. + (only the :meth:`readline` method is used). The file-like object must + operate in text mode, i.e. return strings from :meth:`readline`. + + If *filename* is omitted and *fp* has a :attr:`name` attribute, that is used + for *filename*; the default is ``<???>``. .. method:: RawConfigParser.get(section, option) @@ -279,8 +281,9 @@ RawConfigParser Objects .. method:: RawConfigParser.write(fileobject) - Write a representation of the configuration to the specified file object. This - representation can be parsed by a future :meth:`read` call. + Write a representation of the configuration to the specified file object, + which must be opened in text mode (accepting strings). This representation + can be parsed by a future :meth:`read` call. .. method:: RawConfigParser.remove_option(section, option) @@ -370,7 +373,7 @@ An example of writing to a configuration file:: config.set('Section1', 'foo', '%(bar)s is %(baz)s!') # Writing our configuration file to 'example.cfg' - with open('example.cfg', 'wb') as configfile: + with open('example.cfg', 'w') as configfile: config.write(configfile) An example of reading the configuration file again:: diff --git a/Doc/library/copy.rst b/Doc/library/copy.rst index e6ad857..5ae79aa9 100644 --- a/Doc/library/copy.rst +++ b/Doc/library/copy.rst @@ -4,21 +4,25 @@ .. module:: copy :synopsis: Shallow and deep copy operations. +This module provides generic (shallow and deep) copying operations. -.. index:: - single: copy() (in copy) - single: deepcopy() (in copy) -This module provides generic (shallow and deep) copying operations. +Interface summary: + +.. function:: copy(x) + + Return a shallow copy of *x*. + + +.. function:: deepcopy(x) + + Return a deep copy of *x*. -Interface summary:: - import copy +.. exception:: error - x = copy.copy(y) # make a shallow copy of y - x = copy.deepcopy(y) # make a deep copy of y + Raised for module specific errors. -For module specific errors, :exc:`copy.error` is raised. The difference between shallow and deep copying is only relevant for compound objects (objects that contain other objects, like lists or class instances): diff --git a/Doc/library/marshal.rst b/Doc/library/marshal.rst index 7e1daf3..3b9e3d2 100644 --- a/Doc/library/marshal.rst +++ b/Doc/library/marshal.rst @@ -36,12 +36,14 @@ supports a substantially wider range of objects than marshal. Not all Python object types are supported; in general, only objects whose value is independent from a particular invocation of Python can be written and read by -this module. The following types are supported: ``None``, integers, -floating point numbers, strings, bytes, bytearrays, tuples, lists, sets, -dictionaries, and code objects, where it should be understood that tuples, lists -and dictionaries are only supported as long as the values contained therein are -themselves supported; and recursive lists and dictionaries should not be written -(they will cause infinite loops). +this module. The following types are supported: booleans, integers, floating +point numbers, complex numbers, strings, bytes, bytearrays, tuples, lists, sets, +frozensets, dictionaries, and code objects, where it should be understood that +tuples, lists, sets, frozensets and dictionaries are only supported as long as +the values contained therein are themselves supported; and recursive lists, sets +and dictionaries should not be written (they will cause infinite loops). The +singletons :const:`None`, :const:`Ellipsis` and :exc:`StopIteration` can also be +marshalled and unmarshalled. There are functions that read/write files as well as functions operating on strings. diff --git a/Doc/library/urllib.parse.rst b/Doc/library/urllib.parse.rst index fc23417..f251852 100644 --- a/Doc/library/urllib.parse.rst +++ b/Doc/library/urllib.parse.rst @@ -227,7 +227,8 @@ The :mod:`urllib.parse` module defines the following functions: .. function:: quote(string, safe='/', encoding=None, errors=None) Replace special characters in *string* using the ``%xx`` escape. Letters, - digits, and the characters ``'_.-'`` are never quoted. The optional *safe* + digits, and the characters ``'_.-'`` are never quoted. By default, this + function is intended for quoting the path section of URL. The optional *safe* parameter specifies additional ASCII characters that should not be quoted --- its default value is ``'/'``. diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index ef2f382..e3abeb7 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -666,7 +666,7 @@ Modules of the shared library file. Custom classes - Custon class types are typically created by class definitions (see section + Custom class types are typically created by class definitions (see section :ref:`class`). A class has a namespace implemented by a dictionary object. Class attribute references are translated to lookups in this dictionary, e.g., ``C.x`` is translated to ``C.__dict__["x"]`` (although there are a number of @@ -208,7 +208,9 @@ class Pdb(bdb.Bdb, cmd.Cmd): """Custom displayhook for the exec in default(), which prevents assignment of the _ variable in the builtins. """ - print(repr(obj)) + # reproduce the behavior of the standard displayhook, not printing None + if obj is not None: + print(repr(obj)) def default(self, line): if line[:1] == '!': line = line[1:] diff --git a/Lib/pydoc.py b/Lib/pydoc.py index fe439c2..68771c9 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -1557,7 +1557,7 @@ class Helper: 'global': ('global', 'NAMESPACES'), 'if': ('if', 'TRUTHVALUE'), 'import': ('import', 'MODULES'), - 'in': ('in', 'SEQUENCEMETHODS2'), + 'in': ('in', 'SEQUENCEMETHODS'), 'is': 'COMPARISON', 'lambda': ('lambda', 'FUNCTIONS'), 'not': 'BOOLEAN', @@ -1643,12 +1643,12 @@ class Helper: 'PRECEDENCE': 'EXPRESSIONS', 'OBJECTS': ('objects', 'TYPES'), 'SPECIALMETHODS': ('specialnames', 'BASICMETHODS ATTRIBUTEMETHODS ' - 'CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS ' - 'SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'), + 'CALLABLEMETHODS SEQUENCEMETHODS MAPPINGMETHODS ' + 'NUMBERMETHODS CLASSES'), 'BASICMETHODS': ('customization', 'hash repr str SPECIALMETHODS'), 'ATTRIBUTEMETHODS': ('attribute-access', 'ATTRIBUTES SPECIALMETHODS'), 'CALLABLEMETHODS': ('callable-types', 'CALLS SPECIALMETHODS'), - 'SEQUENCEMETHODS': ('sequence-types', 'SEQUENCES SEQUENCEMETHODS2 ' + 'SEQUENCEMETHODS': ('sequence-types', 'SEQUENCES SEQUENCEMETHODS ' 'SPECIALMETHODS'), 'MAPPINGMETHODS': ('sequence-types', 'MAPPINGS SPECIALMETHODS'), 'NUMBERMETHODS': ('numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT ' @@ -1672,8 +1672,8 @@ class Helper: 'DICTIONARIES': ('typesmapping', 'DICTIONARYLITERALS'), 'DICTIONARYLITERALS': ('dict', 'DICTIONARIES LITERALS'), 'ATTRIBUTES': ('attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'), - 'SUBSCRIPTS': ('subscriptions', 'SEQUENCEMETHODS1'), - 'SLICINGS': ('slicings', 'SEQUENCEMETHODS2'), + 'SUBSCRIPTS': ('subscriptions', 'SEQUENCEMETHODS'), + 'SLICINGS': ('slicings', 'SEQUENCEMETHODS'), 'CALLS': ('calls', 'EXPRESSIONS'), 'POWER': ('power', 'EXPRESSIONS'), 'UNARY': ('unary', 'EXPRESSIONS'), diff --git a/Lib/test/test_funcattrs.py b/Lib/test/test_funcattrs.py index 5765b4e..d1d03a3 100644 --- a/Lib/test/test_funcattrs.py +++ b/Lib/test/test_funcattrs.py @@ -56,8 +56,29 @@ class FunctionPropertiesTest(FuncAttrsTest): self.assertEqual(test(), 3) # self.b always returns 3, arbitrarily def test___globals__(self): - self.assertEqual(self.b.__globals__, globals()) - self.cannot_set_attr(self.b, '__globals__', 2, (AttributeError, TypeError)) + self.assertIs(self.b.__globals__, globals()) + self.cannot_set_attr(self.b, '__globals__', 2, + (AttributeError, TypeError)) + + def test___closure__(self): + a = 12 + def f(): print(a) + c = f.__closure__ + self.assertTrue(isinstance(c, tuple)) + self.assertEqual(len(c), 1) + # don't have a type object handy + self.assertEqual(c[0].__class__.__name__, "cell") + self.cannot_set_attr(f, "__closure__", c, AttributeError) + + def test_empty_cell(self): + def f(): print(a) + try: + f.__closure__[0].cell_contents + except ValueError: + pass + else: + self.fail("shouldn't be able to read an empty cell") + a = 12 def test___name__(self): self.assertEqual(self.b.__name__, 'b') @@ -90,16 +111,20 @@ class FunctionPropertiesTest(FuncAttrsTest): self.assertEqual(c.__code__, d.__code__) self.assertEqual(c(), 7) # self.assertEqual(d(), 7) - try: b.__code__ = c.__code__ - except ValueError: pass - else: self.fail( - "__code__ with different numbers of free vars should not be " - "possible") - try: e.__code__ = d.__code__ - except ValueError: pass - else: self.fail( - "__code__ with different numbers of free vars should not be " - "possible") + try: + b.__code__ = c.__code__ + except ValueError: + pass + else: + self.fail("__code__ with different numbers of free vars should " + "not be possible") + try: + e.__code__ = d.__code__ + except ValueError: + pass + else: + self.fail("__code__ with different numbers of free vars should " + "not be possible") def test_blank_func_defaults(self): self.assertEqual(self.b.__defaults__, None) @@ -120,13 +145,16 @@ class FunctionPropertiesTest(FuncAttrsTest): self.assertEqual(first_func(3, 5), 8) del second_func.__defaults__ self.assertEqual(second_func.__defaults__, None) - try: second_func() - except TypeError: pass - else: self.fail( - "func_defaults does not update; deleting it does not remove " - "requirement") + try: + second_func() + except TypeError: + pass + else: + self.fail("__defaults__ does not update; deleting it does not " + "remove requirement") -class ImplicitReferencesTest(FuncAttrsTest): + +class InstancemethodAttrTest(FuncAttrsTest): def test___class__(self): self.assertEqual(self.fi.a.__self__.__class__, self.F) @@ -146,31 +174,45 @@ class ImplicitReferencesTest(FuncAttrsTest): self.fi.id = types.MethodType(id, self.fi) self.assertEqual(self.fi.id(), id(self.fi)) # Test usage - try: self.fi.id.unknown_attr - except AttributeError: pass - else: self.fail("using unknown attributes should raise AttributeError") + try: + self.fi.id.unknown_attr + except AttributeError: + pass + else: + self.fail("using unknown attributes should raise AttributeError") # Test assignment and deletion self.cannot_set_attr(self.fi.id, 'unknown_attr', 2, AttributeError) + class ArbitraryFunctionAttrTest(FuncAttrsTest): def test_set_attr(self): self.b.known_attr = 7 self.assertEqual(self.b.known_attr, 7) - try: self.fi.a.known_attr = 7 - except AttributeError: pass - else: self.fail("setting attributes on methods should raise error") + try: + self.fi.a.known_attr = 7 + except AttributeError: + pass + else: + self.fail("setting attributes on methods should raise error") def test_delete_unknown_attr(self): - try: del self.b.unknown_attr - except AttributeError: pass - else: self.fail("deleting unknown attribute should raise TypeError") + try: + del self.b.unknown_attr + except AttributeError: + pass + else: + self.fail("deleting unknown attribute should raise TypeError") def test_unset_attr(self): for func in [self.b, self.fi.a]: - try: func.non_existent_attr - except AttributeError: pass - else: self.fail("using unknown attributes should raise " - "AttributeError") + try: + func.non_existent_attr + except AttributeError: + pass + else: + self.fail("using unknown attributes should raise " + "AttributeError") + class FunctionDictsTest(FuncAttrsTest): def test_setting_dict_to_invalid(self): @@ -183,11 +225,11 @@ class FunctionDictsTest(FuncAttrsTest): d = {'known_attr': 7} self.b.__dict__ = d # Test assignment - self.assertEqual(d, self.b.__dict__) + self.assertIs(d, self.b.__dict__) # ... and on all the different ways of referencing the method's func self.F.a.__dict__ = d - self.assertEqual(d, self.fi.a.__func__.__dict__) - self.assertEqual(d, self.fi.a.__dict__) + self.assertIs(d, self.fi.a.__func__.__dict__) + self.assertIs(d, self.fi.a.__dict__) # Test value self.assertEqual(self.b.known_attr, 7) self.assertEqual(self.b.__dict__['known_attr'], 7) @@ -196,9 +238,12 @@ class FunctionDictsTest(FuncAttrsTest): self.assertEqual(self.fi.a.known_attr, 7) def test_delete___dict__(self): - try: del self.b.__dict__ - except TypeError: pass - else: self.fail("deleting function dictionary should raise TypeError") + try: + del self.b.__dict__ + except TypeError: + pass + else: + self.fail("deleting function dictionary should raise TypeError") def test_unassigned_dict(self): self.assertEqual(self.b.__dict__, {}) @@ -209,6 +254,7 @@ class FunctionDictsTest(FuncAttrsTest): d[self.b] = value self.assertEqual(d[self.b], value) + class FunctionDocstringTest(FuncAttrsTest): def test_set_docstring_attr(self): self.assertEqual(self.b.__doc__, None) @@ -224,6 +270,7 @@ class FunctionDocstringTest(FuncAttrsTest): del self.b.__doc__ self.assertEqual(self.b.__doc__, None) + def cell(value): """Create a cell containing the given value.""" def f(): @@ -242,6 +289,7 @@ def empty_cell(empty=True): a = 1729 return f.__closure__[0] + class CellTest(unittest.TestCase): def test_comparison(self): # These tests are here simply to exercise the comparison code; @@ -254,6 +302,7 @@ class CellTest(unittest.TestCase): self.assertTrue(cell(-36) == cell(-36.0)) self.assertTrue(cell(True) > empty_cell()) + class StaticMethodAttrsTest(unittest.TestCase): def test_func_attribute(self): def f(): @@ -267,7 +316,7 @@ class StaticMethodAttrsTest(unittest.TestCase): def test_main(): - support.run_unittest(FunctionPropertiesTest, ImplicitReferencesTest, + support.run_unittest(FunctionPropertiesTest, InstancemethodAttrTest, ArbitraryFunctionAttrTest, FunctionDictsTest, FunctionDocstringTest, CellTest, StaticMethodAttrsTest) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index bcd4853..5d52707 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -12,6 +12,49 @@ from test import support from test.test_doctest import _FakeInput +class PdbTestInput(object): + """Context manager that makes testing Pdb in doctests easier.""" + + def __init__(self, input): + self.input = input + + def __enter__(self): + self.real_stdin = sys.stdin + sys.stdin = _FakeInput(self.input) + + def __exit__(self, *exc): + sys.stdin = self.real_stdin + + +def test_pdb_displayhook(): + """This tests the custom displayhook for pdb. + + >>> def test_function(foo, bar): + ... import pdb; pdb.Pdb().set_trace() + ... pass + + >>> with PdbTestInput([ + ... 'foo', + ... 'bar', + ... 'for i in range(5): print(i)', + ... 'continue', + ... ]): + ... test_function(1, None) + > <doctest test.test_pdb.test_pdb_displayhook[0]>(3)test_function() + -> pass + (Pdb) foo + 1 + (Pdb) bar + (Pdb) for i in range(5): print(i) + 0 + 1 + 2 + 3 + 4 + (Pdb) continue + """ + + def test_pdb_skip_modules(): """This illustrates the simple case of module skipping. @@ -19,16 +62,12 @@ def test_pdb_skip_modules(): ... import string ... import pdb; pdb.Pdb(skip=['stri*']).set_trace() ... string.capwords('FOO') - >>> real_stdin = sys.stdin - >>> sys.stdin = _FakeInput([ - ... 'step', - ... 'continue', - ... ]) - >>> try: + >>> with PdbTestInput([ + ... 'step', + ... 'continue', + ... ]): ... skip_module() - ... finally: - ... sys.stdin = real_stdin > <doctest test.test_pdb.test_pdb_skip_modules[0]>(4)skip_module() -> string.capwords('FOO') (Pdb) step @@ -36,7 +75,7 @@ def test_pdb_skip_modules(): > <doctest test.test_pdb.test_pdb_skip_modules[0]>(4)skip_module()->None -> string.capwords('FOO') (Pdb) continue -""" + """ # Module for testing skipping of module that makes a callback @@ -50,22 +89,19 @@ def test_pdb_skip_modules_with_callback(): >>> def skip_module(): ... def callback(): ... return None - ... import pdb;pdb.Pdb(skip=['module_to_skip*']).set_trace() + ... import pdb; pdb.Pdb(skip=['module_to_skip*']).set_trace() ... mod.foo_pony(callback) - >>> real_stdin = sys.stdin - >>> sys.stdin = _FakeInput([ - ... 'step', - ... 'step', - ... 'step', - ... 'step', - ... 'step', - ... 'continue', - ... ]) - - >>> try: + + >>> with PdbTestInput([ + ... 'step', + ... 'step', + ... 'step', + ... 'step', + ... 'step', + ... 'continue', + ... ]): ... skip_module() - ... finally: - ... sys.stdin = real_stdin + ... pass # provides something to "step" to > <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(5)skip_module() -> mod.foo_pony(callback) (Pdb) step @@ -84,10 +120,10 @@ def test_pdb_skip_modules_with_callback(): > <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(5)skip_module()->None -> mod.foo_pony(callback) (Pdb) step - > <doctest test.test_pdb.test_pdb_skip_modules_with_callback[3]>(4)<module>() - -> sys.stdin = real_stdin + > <doctest test.test_pdb.test_pdb_skip_modules_with_callback[1]>(10)<module>() + -> pass # provides something to "step" to (Pdb) continue -""" + """ def test_main(): |