mirror of
https://github.com/python/cpython.git
synced 2025-08-30 21:48:47 +00:00
More trivial comment -> docstring transformations by Ka-Ping Yee,
who writes: Here is batch 2, as a big collection of CVS context diffs. Along with moving comments into docstrings, i've added a couple of missing docstrings and attempted to make sure more module docstrings begin with a one-line summary. I did not add docstrings to the methods in profile.py for fear of upsetting any careful optimizations there, though i did move class documentation into class docstrings. The convention i'm using is to leave credits/version/copyright type of stuff in # comments, and move the rest of the descriptive stuff about module usage into module docstrings. Hope this is okay.
This commit is contained in:
parent
8b6323d3ef
commit
54f22ed30b
30 changed files with 1547 additions and 1792 deletions
438
Lib/dospath.py
438
Lib/dospath.py
|
@ -1,64 +1,64 @@
|
||||||
# Module 'dospath' -- common operations on DOS pathnames
|
"""Module 'dospath' -- common operations on DOS pathnames"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import stat
|
import stat
|
||||||
import string
|
import string
|
||||||
|
|
||||||
|
|
||||||
# Normalize the case of a pathname.
|
|
||||||
# On MS-DOS it maps the pathname to lowercase, turns slashes into
|
|
||||||
# backslashes.
|
|
||||||
# Other normalizations (such as optimizing '../' away) are not allowed
|
|
||||||
# (this is done by normpath).
|
|
||||||
# Previously, this version mapped invalid consecutive characters to a
|
|
||||||
# single '_', but this has been removed. This functionality should
|
|
||||||
# possibly be added as a new function.
|
|
||||||
|
|
||||||
def normcase(s):
|
def normcase(s):
|
||||||
return string.lower(string.replace(s, "/", "\\"))
|
"""Normalize the case of a pathname.
|
||||||
|
On MS-DOS it maps the pathname to lowercase, turns slashes into
|
||||||
|
backslashes.
|
||||||
|
Other normalizations (such as optimizing '../' away) are not allowed
|
||||||
|
(this is done by normpath).
|
||||||
|
Previously, this version mapped invalid consecutive characters to a
|
||||||
|
single '_', but this has been removed. This functionality should
|
||||||
|
possibly be added as a new function."""
|
||||||
|
|
||||||
|
return string.lower(string.replace(s, "/", "\\"))
|
||||||
|
|
||||||
# Return wheter a path is absolute.
|
|
||||||
# Trivial in Posix, harder on the Mac or MS-DOS.
|
|
||||||
# For DOS it is absolute if it starts with a slash or backslash (current
|
|
||||||
# volume), or if a pathname after the volume letter and colon starts with
|
|
||||||
# a slash or backslash.
|
|
||||||
|
|
||||||
def isabs(s):
|
def isabs(s):
|
||||||
s = splitdrive(s)[1]
|
"""Return whether a path is absolute.
|
||||||
return s != '' and s[:1] in '/\\'
|
Trivial in Posix, harder on the Mac or MS-DOS.
|
||||||
|
For DOS it is absolute if it starts with a slash or backslash (current
|
||||||
|
volume), or if a pathname after the volume letter and colon starts with
|
||||||
|
a slash or backslash."""
|
||||||
|
|
||||||
|
s = splitdrive(s)[1]
|
||||||
|
return s != '' and s[:1] in '/\\'
|
||||||
|
|
||||||
# Join two (or more) paths.
|
|
||||||
|
|
||||||
def join(a, *p):
|
def join(a, *p):
|
||||||
path = a
|
"""Join two (or more) paths."""
|
||||||
for b in p:
|
|
||||||
if isabs(b):
|
|
||||||
path = b
|
|
||||||
elif path == '' or path[-1:] in '/\\':
|
|
||||||
path = path + b
|
|
||||||
else:
|
|
||||||
path = path + os.sep + b
|
|
||||||
return path
|
|
||||||
|
|
||||||
|
path = a
|
||||||
|
for b in p:
|
||||||
|
if isabs(b):
|
||||||
|
path = b
|
||||||
|
elif path == '' or path[-1:] in '/\\':
|
||||||
|
path = path + b
|
||||||
|
else:
|
||||||
|
path = path + os.sep + b
|
||||||
|
return path
|
||||||
|
|
||||||
# Split a path in a drive specification (a drive letter followed by a
|
|
||||||
# colon) and the path specification.
|
|
||||||
# It is always true that drivespec + pathspec == p
|
|
||||||
|
|
||||||
def splitdrive(p):
|
def splitdrive(p):
|
||||||
if p[1:2] == ':':
|
"""Split a path into a drive specification (a drive letter followed
|
||||||
return p[0:2], p[2:]
|
by a colon) and path specification.
|
||||||
return '', p
|
It is always true that drivespec + pathspec == p."""
|
||||||
|
|
||||||
|
if p[1:2] == ':':
|
||||||
|
return p[0:2], p[2:]
|
||||||
|
return '', p
|
||||||
|
|
||||||
# Split a path in head (everything up to the last '/') and tail (the
|
|
||||||
# rest). After the trailing '/' is stripped, the invariant
|
|
||||||
# join(head, tail) == p holds.
|
|
||||||
# The resulting head won't end in '/' unless it is the root.
|
|
||||||
|
|
||||||
def split(p):
|
def split(p):
|
||||||
|
"""Split a path into head (everything up to the last '/') and tail
|
||||||
|
(the rest). After the trailing '/' is stripped, the invariant
|
||||||
|
join(head, tail) == p holds.
|
||||||
|
The resulting head won't end in '/' unless it is the root."""
|
||||||
|
|
||||||
d, p = splitdrive(p)
|
d, p = splitdrive(p)
|
||||||
# set i to index beyond p's last slash
|
# set i to index beyond p's last slash
|
||||||
i = len(p)
|
i = len(p)
|
||||||
|
@ -73,47 +73,47 @@ def split(p):
|
||||||
return d + head, tail
|
return d + head, tail
|
||||||
|
|
||||||
|
|
||||||
# Split a path in root and extension.
|
|
||||||
# The extension is everything starting at the first dot in the last
|
|
||||||
# pathname component; the root is everything before that.
|
|
||||||
# It is always true that root + ext == p.
|
|
||||||
|
|
||||||
def splitext(p):
|
def splitext(p):
|
||||||
root, ext = '', ''
|
"""Split a path into root and extension.
|
||||||
for c in p:
|
The extension is everything starting at the first dot in the last
|
||||||
if c in '/\\':
|
pathname component; the root is everything before that.
|
||||||
root, ext = root + ext + c, ''
|
It is always true that root + ext == p."""
|
||||||
elif c == '.' or ext:
|
|
||||||
ext = ext + c
|
|
||||||
else:
|
|
||||||
root = root + c
|
|
||||||
return root, ext
|
|
||||||
|
|
||||||
|
root, ext = '', ''
|
||||||
|
for c in p:
|
||||||
|
if c in '/\\':
|
||||||
|
root, ext = root + ext + c, ''
|
||||||
|
elif c == '.' or ext:
|
||||||
|
ext = ext + c
|
||||||
|
else:
|
||||||
|
root = root + c
|
||||||
|
return root, ext
|
||||||
|
|
||||||
# Return the tail (basename) part of a path.
|
|
||||||
|
|
||||||
def basename(p):
|
def basename(p):
|
||||||
return split(p)[1]
|
"""Return the tail (basename) part of a path."""
|
||||||
|
|
||||||
|
return split(p)[1]
|
||||||
|
|
||||||
# Return the head (dirname) part of a path.
|
|
||||||
|
|
||||||
def dirname(p):
|
def dirname(p):
|
||||||
return split(p)[0]
|
"""Return the head (dirname) part of a path."""
|
||||||
|
|
||||||
|
return split(p)[0]
|
||||||
|
|
||||||
# Return the longest prefix of all list elements.
|
|
||||||
|
|
||||||
def commonprefix(m):
|
def commonprefix(m):
|
||||||
if not m: return ''
|
"""Return the longest prefix of all list elements."""
|
||||||
prefix = m[0]
|
|
||||||
for item in m:
|
if not m: return ''
|
||||||
for i in range(len(prefix)):
|
prefix = m[0]
|
||||||
if prefix[:i+1] <> item[:i+1]:
|
for item in m:
|
||||||
prefix = prefix[:i]
|
for i in range(len(prefix)):
|
||||||
if i == 0: return ''
|
if prefix[:i+1] <> item[:i+1]:
|
||||||
break
|
prefix = prefix[:i]
|
||||||
return prefix
|
if i == 0: return ''
|
||||||
|
break
|
||||||
|
return prefix
|
||||||
|
|
||||||
|
|
||||||
# Get size, mtime, atime of files.
|
# Get size, mtime, atime of files.
|
||||||
|
@ -134,200 +134,196 @@ def getatime(filename):
|
||||||
return st[stat.ST_MTIME]
|
return st[stat.ST_MTIME]
|
||||||
|
|
||||||
|
|
||||||
# Is a path a symbolic link?
|
|
||||||
# This will always return false on systems where posix.lstat doesn't exist.
|
|
||||||
|
|
||||||
def islink(path):
|
def islink(path):
|
||||||
return 0
|
"""Is a path a symbolic link?
|
||||||
|
This will always return false on systems where posix.lstat doesn't exist."""
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
# Does a path exist?
|
|
||||||
# This is false for dangling symbolic links.
|
|
||||||
|
|
||||||
def exists(path):
|
def exists(path):
|
||||||
try:
|
"""Does a path exist?
|
||||||
st = os.stat(path)
|
This is false for dangling symbolic links."""
|
||||||
except os.error:
|
|
||||||
return 0
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
st = os.stat(path)
|
||||||
|
except os.error:
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
|
||||||
# Is a path a dos directory?
|
|
||||||
# This follows symbolic links, so both islink() and isdir() can be true
|
|
||||||
# for the same path.
|
|
||||||
|
|
||||||
def isdir(path):
|
def isdir(path):
|
||||||
try:
|
"""Is a path a dos directory?"""
|
||||||
st = os.stat(path)
|
|
||||||
except os.error:
|
|
||||||
return 0
|
|
||||||
return stat.S_ISDIR(st[stat.ST_MODE])
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
st = os.stat(path)
|
||||||
|
except os.error:
|
||||||
|
return 0
|
||||||
|
return stat.S_ISDIR(st[stat.ST_MODE])
|
||||||
|
|
||||||
# Is a path a regular file?
|
|
||||||
# This follows symbolic links, so both islink() and isdir() can be true
|
|
||||||
# for the same path.
|
|
||||||
|
|
||||||
def isfile(path):
|
def isfile(path):
|
||||||
try:
|
"""Is a path a regular file?"""
|
||||||
st = os.stat(path)
|
|
||||||
except os.error:
|
|
||||||
return 0
|
|
||||||
return stat.S_ISREG(st[stat.ST_MODE])
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
st = os.stat(path)
|
||||||
|
except os.error:
|
||||||
|
return 0
|
||||||
|
return stat.S_ISREG(st[stat.ST_MODE])
|
||||||
|
|
||||||
# Is a path a mount point?
|
|
||||||
# XXX This degenerates in: 'is this the root?' on DOS
|
|
||||||
|
|
||||||
def ismount(path):
|
def ismount(path):
|
||||||
return isabs(splitdrive(path)[1])
|
"""Is a path a mount point?"""
|
||||||
|
# XXX This degenerates in: 'is this the root?' on DOS
|
||||||
|
|
||||||
|
return isabs(splitdrive(path)[1])
|
||||||
|
|
||||||
# Directory tree walk.
|
|
||||||
# For each directory under top (including top itself, but excluding
|
|
||||||
# '.' and '..'), func(arg, dirname, filenames) is called, where
|
|
||||||
# dirname is the name of the directory and filenames is the list
|
|
||||||
# files files (and subdirectories etc.) in the directory.
|
|
||||||
# The func may modify the filenames list, to implement a filter,
|
|
||||||
# or to impose a different order of visiting.
|
|
||||||
|
|
||||||
def walk(top, func, arg):
|
def walk(top, func, arg):
|
||||||
try:
|
"""Directory tree walk.
|
||||||
names = os.listdir(top)
|
For each directory under top (including top itself, but excluding
|
||||||
except os.error:
|
'.' and '..'), func(arg, dirname, filenames) is called, where
|
||||||
return
|
dirname is the name of the directory and filenames is the list
|
||||||
func(arg, top, names)
|
files files (and subdirectories etc.) in the directory.
|
||||||
exceptions = ('.', '..')
|
The func may modify the filenames list, to implement a filter,
|
||||||
for name in names:
|
or to impose a different order of visiting."""
|
||||||
if name not in exceptions:
|
|
||||||
name = join(top, name)
|
|
||||||
if isdir(name):
|
|
||||||
walk(name, func, arg)
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
names = os.listdir(top)
|
||||||
|
except os.error:
|
||||||
|
return
|
||||||
|
func(arg, top, names)
|
||||||
|
exceptions = ('.', '..')
|
||||||
|
for name in names:
|
||||||
|
if name not in exceptions:
|
||||||
|
name = join(top, name)
|
||||||
|
if isdir(name):
|
||||||
|
walk(name, func, arg)
|
||||||
|
|
||||||
# Expand paths beginning with '~' or '~user'.
|
|
||||||
# '~' means $HOME; '~user' means that user's home directory.
|
|
||||||
# If the path doesn't begin with '~', or if the user or $HOME is unknown,
|
|
||||||
# the path is returned unchanged (leaving error reporting to whatever
|
|
||||||
# function is called with the expanded path as argument).
|
|
||||||
# See also module 'glob' for expansion of *, ? and [...] in pathnames.
|
|
||||||
# (A function should also be defined to do full *sh-style environment
|
|
||||||
# variable expansion.)
|
|
||||||
|
|
||||||
def expanduser(path):
|
def expanduser(path):
|
||||||
if path[:1] <> '~':
|
"""Expand paths beginning with '~' or '~user'.
|
||||||
return path
|
'~' means $HOME; '~user' means that user's home directory.
|
||||||
i, n = 1, len(path)
|
If the path doesn't begin with '~', or if the user or $HOME is unknown,
|
||||||
while i < n and path[i] not in '/\\':
|
the path is returned unchanged (leaving error reporting to whatever
|
||||||
i = i+1
|
function is called with the expanded path as argument).
|
||||||
if i == 1:
|
See also module 'glob' for expansion of *, ? and [...] in pathnames.
|
||||||
if not os.environ.has_key('HOME'):
|
(A function should also be defined to do full *sh-style environment
|
||||||
return path
|
variable expansion.)"""
|
||||||
userhome = os.environ['HOME']
|
|
||||||
else:
|
|
||||||
return path
|
|
||||||
return userhome + path[i:]
|
|
||||||
|
|
||||||
|
if path[:1] <> '~':
|
||||||
|
return path
|
||||||
|
i, n = 1, len(path)
|
||||||
|
while i < n and path[i] not in '/\\':
|
||||||
|
i = i+1
|
||||||
|
if i == 1:
|
||||||
|
if not os.environ.has_key('HOME'):
|
||||||
|
return path
|
||||||
|
userhome = os.environ['HOME']
|
||||||
|
else:
|
||||||
|
return path
|
||||||
|
return userhome + path[i:]
|
||||||
|
|
||||||
# Expand paths containing shell variable substitutions.
|
|
||||||
# The following rules apply:
|
|
||||||
# - no expansion within single quotes
|
|
||||||
# - no escape character, except for '$$' which is translated into '$'
|
|
||||||
# - ${varname} is accepted.
|
|
||||||
# - varnames can be made out of letters, digits and the character '_'
|
|
||||||
# XXX With COMMAND.COM you can use any characters in a variable name,
|
|
||||||
# XXX except '^|<>='.
|
|
||||||
|
|
||||||
varchars = string.letters + string.digits + '_-'
|
varchars = string.letters + string.digits + '_-'
|
||||||
|
|
||||||
def expandvars(path):
|
def expandvars(path):
|
||||||
if '$' not in path:
|
"""Expand paths containing shell variable substitutions.
|
||||||
return path
|
The following rules apply:
|
||||||
res = ''
|
- no expansion within single quotes
|
||||||
index = 0
|
- no escape character, except for '$$' which is translated into '$'
|
||||||
pathlen = len(path)
|
- ${varname} is accepted.
|
||||||
while index < pathlen:
|
- varnames can be made out of letters, digits and the character '_'"""
|
||||||
c = path[index]
|
# XXX With COMMAND.COM you can use any characters in a variable name,
|
||||||
if c == '\'': # no expansion within single quotes
|
# XXX except '^|<>='.
|
||||||
path = path[index + 1:]
|
|
||||||
pathlen = len(path)
|
|
||||||
try:
|
|
||||||
index = string.index(path, '\'')
|
|
||||||
res = res + '\'' + path[:index + 1]
|
|
||||||
except string.index_error:
|
|
||||||
res = res + path
|
|
||||||
index = pathlen -1
|
|
||||||
elif c == '$': # variable or '$$'
|
|
||||||
if path[index + 1:index + 2] == '$':
|
|
||||||
res = res + c
|
|
||||||
index = index + 1
|
|
||||||
elif path[index + 1:index + 2] == '{':
|
|
||||||
path = path[index+2:]
|
|
||||||
pathlen = len(path)
|
|
||||||
try:
|
|
||||||
index = string.index(path, '}')
|
|
||||||
var = path[:index]
|
|
||||||
if os.environ.has_key(var):
|
|
||||||
res = res + os.environ[var]
|
|
||||||
except string.index_error:
|
|
||||||
res = res + path
|
|
||||||
index = pathlen - 1
|
|
||||||
else:
|
|
||||||
var = ''
|
|
||||||
index = index + 1
|
|
||||||
c = path[index:index + 1]
|
|
||||||
while c != '' and c in varchars:
|
|
||||||
var = var + c
|
|
||||||
index = index + 1
|
|
||||||
c = path[index:index + 1]
|
|
||||||
if os.environ.has_key(var):
|
|
||||||
res = res + os.environ[var]
|
|
||||||
if c != '':
|
|
||||||
res = res + c
|
|
||||||
else:
|
|
||||||
res = res + c
|
|
||||||
index = index + 1
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
if '$' not in path:
|
||||||
|
return path
|
||||||
|
res = ''
|
||||||
|
index = 0
|
||||||
|
pathlen = len(path)
|
||||||
|
while index < pathlen:
|
||||||
|
c = path[index]
|
||||||
|
if c == '\'': # no expansion within single quotes
|
||||||
|
path = path[index + 1:]
|
||||||
|
pathlen = len(path)
|
||||||
|
try:
|
||||||
|
index = string.index(path, '\'')
|
||||||
|
res = res + '\'' + path[:index + 1]
|
||||||
|
except string.index_error:
|
||||||
|
res = res + path
|
||||||
|
index = pathlen -1
|
||||||
|
elif c == '$': # variable or '$$'
|
||||||
|
if path[index + 1:index + 2] == '$':
|
||||||
|
res = res + c
|
||||||
|
index = index + 1
|
||||||
|
elif path[index + 1:index + 2] == '{':
|
||||||
|
path = path[index+2:]
|
||||||
|
pathlen = len(path)
|
||||||
|
try:
|
||||||
|
index = string.index(path, '}')
|
||||||
|
var = path[:index]
|
||||||
|
if os.environ.has_key(var):
|
||||||
|
res = res + os.environ[var]
|
||||||
|
except string.index_error:
|
||||||
|
res = res + path
|
||||||
|
index = pathlen - 1
|
||||||
|
else:
|
||||||
|
var = ''
|
||||||
|
index = index + 1
|
||||||
|
c = path[index:index + 1]
|
||||||
|
while c != '' and c in varchars:
|
||||||
|
var = var + c
|
||||||
|
index = index + 1
|
||||||
|
c = path[index:index + 1]
|
||||||
|
if os.environ.has_key(var):
|
||||||
|
res = res + os.environ[var]
|
||||||
|
if c != '':
|
||||||
|
res = res + c
|
||||||
|
else:
|
||||||
|
res = res + c
|
||||||
|
index = index + 1
|
||||||
|
return res
|
||||||
|
|
||||||
# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
|
|
||||||
# Also, components of the path are silently truncated to 8+3 notation.
|
|
||||||
|
|
||||||
def normpath(path):
|
def normpath(path):
|
||||||
path = string.replace(path, "/", "\\")
|
"""Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
|
||||||
prefix, path = splitdrive(path)
|
Also, components of the path are silently truncated to 8+3 notation."""
|
||||||
while path[:1] == os.sep:
|
|
||||||
prefix = prefix + os.sep
|
path = string.replace(path, "/", "\\")
|
||||||
path = path[1:]
|
prefix, path = splitdrive(path)
|
||||||
comps = string.splitfields(path, os.sep)
|
while path[:1] == os.sep:
|
||||||
i = 0
|
prefix = prefix + os.sep
|
||||||
while i < len(comps):
|
path = path[1:]
|
||||||
if comps[i] == '.':
|
comps = string.splitfields(path, os.sep)
|
||||||
del comps[i]
|
i = 0
|
||||||
elif comps[i] == '..' and i > 0 and \
|
while i < len(comps):
|
||||||
comps[i-1] not in ('', '..'):
|
if comps[i] == '.':
|
||||||
del comps[i-1:i+1]
|
del comps[i]
|
||||||
i = i-1
|
elif comps[i] == '..' and i > 0 and \
|
||||||
elif comps[i] == '' and i > 0 and comps[i-1] <> '':
|
comps[i-1] not in ('', '..'):
|
||||||
del comps[i]
|
del comps[i-1:i+1]
|
||||||
elif '.' in comps[i]:
|
i = i-1
|
||||||
comp = string.splitfields(comps[i], '.')
|
elif comps[i] == '' and i > 0 and comps[i-1] <> '':
|
||||||
comps[i] = comp[0][:8] + '.' + comp[1][:3]
|
del comps[i]
|
||||||
i = i+1
|
elif '.' in comps[i]:
|
||||||
elif len(comps[i]) > 8:
|
comp = string.splitfields(comps[i], '.')
|
||||||
comps[i] = comps[i][:8]
|
comps[i] = comp[0][:8] + '.' + comp[1][:3]
|
||||||
i = i+1
|
i = i+1
|
||||||
else:
|
elif len(comps[i]) > 8:
|
||||||
i = i+1
|
comps[i] = comps[i][:8]
|
||||||
# If the path is now empty, substitute '.'
|
i = i+1
|
||||||
if not prefix and not comps:
|
else:
|
||||||
comps.append('.')
|
i = i+1
|
||||||
return prefix + string.joinfields(comps, os.sep)
|
# If the path is now empty, substitute '.'
|
||||||
|
if not prefix and not comps:
|
||||||
|
comps.append('.')
|
||||||
|
return prefix + string.joinfields(comps, os.sep)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Return an absolute path.
|
|
||||||
def abspath(path):
|
def abspath(path):
|
||||||
|
"""Return an absolute path."""
|
||||||
if not isabs(path):
|
if not isabs(path):
|
||||||
path = join(os.getcwd(), path)
|
path = join(os.getcwd(), path)
|
||||||
return normpath(path)
|
return normpath(path)
|
||||||
|
|
337
Lib/filecmp.py
337
Lib/filecmp.py
|
@ -1,318 +1,57 @@
|
||||||
"""Utilities for comparing files and directories.
|
"""Compare files."""
|
||||||
|
|
||||||
Classes:
|
import os, stat, statcache
|
||||||
dircmp
|
|
||||||
|
|
||||||
Functions:
|
|
||||||
cmp(f1, f2, shallow=1, use_statcache=0) -> int
|
|
||||||
cmpfiles(a, b, common) -> ([], [], [])
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import stat
|
|
||||||
import statcache
|
|
||||||
|
|
||||||
_cache = {}
|
_cache = {}
|
||||||
BUFSIZE=8*1024
|
BUFSIZE=8*1024
|
||||||
|
|
||||||
def cmp(f1, f2, shallow=1,use_statcache=0):
|
def cmp(f1, f2, shallow=1,use_statcache=0):
|
||||||
"""Compare two files.
|
"""Compare two files.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
|
|
||||||
f1 -- First file name
|
f1 -- First file name
|
||||||
|
|
||||||
f2 -- Second file name
|
f2 -- Second file name
|
||||||
|
|
||||||
shallow -- Just check stat signature (do not read the files).
|
shallow -- Just check stat signature (do not read the files).
|
||||||
defaults to 1.
|
defaults to 1.
|
||||||
|
|
||||||
use_statcache -- Do not stat() each file directly: go through
|
use_statcache -- Do not stat() each file directly: go through
|
||||||
the statcache module for more efficiency.
|
the statcache module for more efficiency.
|
||||||
|
|
||||||
Return value:
|
Return value:
|
||||||
|
|
||||||
integer -- 1 if the files are the same, 0 otherwise.
|
integer -- 1 if the files are the same, 0 otherwise.
|
||||||
|
|
||||||
This function uses a cache for past comparisons and the results,
|
This function uses a cache for past comparisons and the results,
|
||||||
with a cache invalidation mechanism relying on stale signatures.
|
with a cache invalidation mechanism relying on stale signatures.
|
||||||
Of course, if 'use_statcache' is true, this mechanism is defeated,
|
Of course, if 'use_statcache' is true, this mechanism is defeated,
|
||||||
and the cache will never grow stale.
|
and the cache will never grow stale.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
stat_function = (os.stat, statcache.stat)[use_statcache]
|
stat_function = (os.stat, statcache.stat)[use_statcache]
|
||||||
s1, s2 = _sig(stat_function(f1)), _sig(stat_function(f2))
|
s1, s2 = _sig(stat_function(f1)), _sig(stat_function(f2))
|
||||||
if s1[0]!=stat.S_IFREG or s2[0]!=stat.S_IFREG: return 0
|
if s1[0]!=stat.S_IFREG or s2[0]!=stat.S_IFREG: return 0
|
||||||
if shallow and s1 == s2: return 1
|
if shallow and s1 == s2: return 1
|
||||||
if s1[1]!=s2[1]: return 0
|
if s1[1]!=s2[1]: return 0
|
||||||
|
|
||||||
result = _cache.get((f1, f2))
|
result = _cache.get((f1, f2))
|
||||||
if result and (s1, s2)==result[:2]:
|
if result and (s1, s2)==result[:2]:
|
||||||
return result[2]
|
return result[2]
|
||||||
outcome = _do_cmp(f1, f2)
|
outcome = _do_cmp(f1, f2)
|
||||||
_cache[f1, f2] = s1, s2, outcome
|
_cache[f1, f2] = s1, s2, outcome
|
||||||
return outcome
|
return outcome
|
||||||
|
|
||||||
def _sig(st):
|
def _sig(st):
|
||||||
return (stat.S_IFMT(st[stat.ST_MODE]),
|
return (stat.S_IFMT(st[stat.ST_MODE]),
|
||||||
st[stat.ST_SIZE],
|
st[stat.ST_SIZE],
|
||||||
st[stat.ST_MTIME])
|
st[stat.ST_MTIME])
|
||||||
|
|
||||||
def _do_cmp(f1, f2):
|
def _do_cmp(f1, f2):
|
||||||
bufsize = BUFSIZE
|
bufsize = BUFSIZE
|
||||||
fp1 , fp2 = open(f1, 'rb'), open(f2, 'rb')
|
fp1 , fp2 = open(f1, 'rb'), open(f2, 'rb')
|
||||||
while 1:
|
while 1:
|
||||||
b1, b2 = fp1.read(bufsize), fp2.read(bufsize)
|
b1, b2 = fp1.read(bufsize), fp2.read(bufsize)
|
||||||
if b1!=b2: return 0
|
if b1!=b2: return 0
|
||||||
if not b1: return 1
|
if not b1: return 1
|
||||||
|
|
||||||
# Directory comparison class.
|
|
||||||
#
|
|
||||||
class dircmp:
|
|
||||||
"""A class that manages the comparison of 2 directories.
|
|
||||||
|
|
||||||
dircmp(a,b,ignore=None,hide=None)
|
|
||||||
A and B are directories.
|
|
||||||
IGNORE is a list of names to ignore,
|
|
||||||
defaults to ['RCS', 'CVS', 'tags'].
|
|
||||||
HIDE is a list of names to hide,
|
|
||||||
defaults to [os.curdir, os.pardir].
|
|
||||||
|
|
||||||
High level usage:
|
|
||||||
x = dircmp(dir1, dir2)
|
|
||||||
x.report() -> prints a report on the differences between dir1 and dir2
|
|
||||||
or
|
|
||||||
x.report_partial_closure() -> prints report on differences between dir1
|
|
||||||
and dir2, and reports on common immediate subdirectories.
|
|
||||||
x.report_full_closure() -> like report_partial_closure,
|
|
||||||
but fully recursive.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
left_list, right_list: The files in dir1 and dir2,
|
|
||||||
filtered by hide and ignore.
|
|
||||||
common: a list of names in both dir1 and dir2.
|
|
||||||
left_only, right_only: names only in dir1, dir2.
|
|
||||||
common_dirs: subdirectories in both dir1 and dir2.
|
|
||||||
common_files: files in both dir1 and dir2.
|
|
||||||
common_funny: names in both dir1 and dir2 where the type differs between
|
|
||||||
dir1 and dir2, or the name is not stat-able.
|
|
||||||
same_files: list of identical files.
|
|
||||||
diff_files: list of filenames which differ.
|
|
||||||
funny_files: list of files which could not be compared.
|
|
||||||
subdirs: a dictionary of dircmp objects, keyed by names in common_dirs.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, a, b, ignore=None, hide=None): # Initialize
|
|
||||||
self.left = a
|
|
||||||
self.right = b
|
|
||||||
if hide is None:
|
|
||||||
self.hide = [os.curdir, os.pardir] # Names never to be shown
|
|
||||||
else:
|
|
||||||
self.hide = hide
|
|
||||||
if ignore is None:
|
|
||||||
self.ignore = ['RCS', 'CVS', 'tags'] # Names ignored in comparison
|
|
||||||
else:
|
|
||||||
self.ignore = ignore
|
|
||||||
|
|
||||||
def phase0(self): # Compare everything except common subdirectories
|
|
||||||
self.left_list = _filter(os.listdir(self.left),
|
|
||||||
self.hide+self.ignore)
|
|
||||||
self.right_list = _filter(os.listdir(self.right),
|
|
||||||
self.hide+self.ignore)
|
|
||||||
self.left_list.sort()
|
|
||||||
self.right_list.sort()
|
|
||||||
|
|
||||||
__p4_attrs = ('subdirs',)
|
|
||||||
__p3_attrs = ('same_files', 'diff_files', 'funny_files')
|
|
||||||
__p2_attrs = ('common_dirs', 'common_files', 'common_funny')
|
|
||||||
__p1_attrs = ('common', 'left_only', 'right_only')
|
|
||||||
__p0_attrs = ('left_list', 'right_list')
|
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
|
||||||
if attr in self.__p4_attrs:
|
|
||||||
self.phase4()
|
|
||||||
elif attr in self.__p3_attrs:
|
|
||||||
self.phase3()
|
|
||||||
elif attr in self.__p2_attrs:
|
|
||||||
self.phase2()
|
|
||||||
elif attr in self.__p1_attrs:
|
|
||||||
self.phase1()
|
|
||||||
elif attr in self.__p0_attrs:
|
|
||||||
self.phase0()
|
|
||||||
else:
|
|
||||||
raise AttributeError, attr
|
|
||||||
return getattr(self, attr)
|
|
||||||
|
|
||||||
def phase1(self): # Compute common names
|
|
||||||
a_only, b_only = [], []
|
|
||||||
common = {}
|
|
||||||
b = {}
|
|
||||||
for fnm in self.right_list:
|
|
||||||
b[fnm] = 1
|
|
||||||
for x in self.left_list:
|
|
||||||
if b.get(x, 0):
|
|
||||||
common[x] = 1
|
|
||||||
else:
|
|
||||||
a_only.append(x)
|
|
||||||
for x in self.right_list:
|
|
||||||
if common.get(x, 0):
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
b_only.append(x)
|
|
||||||
self.common = common.keys()
|
|
||||||
self.left_only = a_only
|
|
||||||
self.right_only = b_only
|
|
||||||
|
|
||||||
def phase2(self): # Distinguish files, directories, funnies
|
|
||||||
self.common_dirs = []
|
|
||||||
self.common_files = []
|
|
||||||
self.common_funny = []
|
|
||||||
|
|
||||||
for x in self.common:
|
|
||||||
a_path = os.path.join(self.left, x)
|
|
||||||
b_path = os.path.join(self.right, x)
|
|
||||||
|
|
||||||
ok = 1
|
|
||||||
try:
|
|
||||||
a_stat = statcache.stat(a_path)
|
|
||||||
except os.error, why:
|
|
||||||
# print 'Can\'t stat', a_path, ':', why[1]
|
|
||||||
ok = 0
|
|
||||||
try:
|
|
||||||
b_stat = statcache.stat(b_path)
|
|
||||||
except os.error, why:
|
|
||||||
# print 'Can\'t stat', b_path, ':', why[1]
|
|
||||||
ok = 0
|
|
||||||
|
|
||||||
if ok:
|
|
||||||
a_type = stat.S_IFMT(a_stat[stat.ST_MODE])
|
|
||||||
b_type = stat.S_IFMT(b_stat[stat.ST_MODE])
|
|
||||||
if a_type <> b_type:
|
|
||||||
self.common_funny.append(x)
|
|
||||||
elif stat.S_ISDIR(a_type):
|
|
||||||
self.common_dirs.append(x)
|
|
||||||
elif stat.S_ISREG(a_type):
|
|
||||||
self.common_files.append(x)
|
|
||||||
else:
|
|
||||||
self.common_funny.append(x)
|
|
||||||
else:
|
|
||||||
self.common_funny.append(x)
|
|
||||||
|
|
||||||
def phase3(self): # Find out differences between common files
|
|
||||||
xx = cmpfiles(self.left, self.right, self.common_files)
|
|
||||||
self.same_files, self.diff_files, self.funny_files = xx
|
|
||||||
|
|
||||||
def phase4(self): # Find out differences between common subdirectories
|
|
||||||
# A new dircmp object is created for each common subdirectory,
|
|
||||||
# these are stored in a dictionary indexed by filename.
|
|
||||||
# The hide and ignore properties are inherited from the parent
|
|
||||||
self.subdirs = {}
|
|
||||||
for x in self.common_dirs:
|
|
||||||
a_x = os.path.join(self.left, x)
|
|
||||||
b_x = os.path.join(self.right, x)
|
|
||||||
self.subdirs[x] = dircmp(a_x, b_x, self.ignore, self.hide)
|
|
||||||
|
|
||||||
def phase4_closure(self): # Recursively call phase4() on subdirectories
|
|
||||||
self.phase4()
|
|
||||||
for x in self.subdirs.keys():
|
|
||||||
self.subdirs[x].phase4_closure()
|
|
||||||
|
|
||||||
def report(self): # Print a report on the differences between a and b
|
|
||||||
# Output format is purposely lousy
|
|
||||||
print 'diff', self.left, self.right
|
|
||||||
if self.left_only:
|
|
||||||
self.left_only.sort()
|
|
||||||
print 'Only in', self.left, ':', self.left_only
|
|
||||||
if self.right_only:
|
|
||||||
self.right_only.sort()
|
|
||||||
print 'Only in', self.right, ':', self.right_only
|
|
||||||
if self.same_files:
|
|
||||||
self.same_files.sort()
|
|
||||||
print 'Identical files :', self.same_files
|
|
||||||
if self.diff_files:
|
|
||||||
self.diff_files.sort()
|
|
||||||
print 'Differing files :', self.diff_files
|
|
||||||
if self.funny_files:
|
|
||||||
self.funny_files.sort()
|
|
||||||
print 'Trouble with common files :', self.funny_files
|
|
||||||
if self.common_dirs:
|
|
||||||
self.common_dirs.sort()
|
|
||||||
print 'Common subdirectories :', self.common_dirs
|
|
||||||
if self.common_funny:
|
|
||||||
self.common_funny.sort()
|
|
||||||
print 'Common funny cases :', self.common_funny
|
|
||||||
|
|
||||||
def report_partial_closure(self): # Print reports on self and on subdirs
|
|
||||||
self.report()
|
|
||||||
for x in self.subdirs.keys():
|
|
||||||
print
|
|
||||||
self.subdirs[x].report()
|
|
||||||
|
|
||||||
def report_full_closure(self): # Report on self and subdirs recursively
|
|
||||||
self.report()
|
|
||||||
for x in self.subdirs.keys():
|
|
||||||
print
|
|
||||||
self.subdirs[x].report_full_closure()
|
|
||||||
|
|
||||||
|
|
||||||
# Compare common files in two directories.
|
|
||||||
# Return:
|
|
||||||
# - files that compare equal
|
|
||||||
# - files that compare different
|
|
||||||
# - funny cases (can't stat etc.)
|
|
||||||
#
|
|
||||||
def cmpfiles(a, b, common):
|
|
||||||
"""Compare common files in two directories.
|
|
||||||
|
|
||||||
cmpfiles(a,b,common)
|
|
||||||
A and B are directory names
|
|
||||||
COMMON is a list of file names
|
|
||||||
returns a tuple of three lists:
|
|
||||||
files that compare equal
|
|
||||||
files that are different
|
|
||||||
filenames that aren't regular files."""
|
|
||||||
|
|
||||||
res = ([], [], [])
|
|
||||||
for x in common:
|
|
||||||
res[_cmp(os.path.join(a, x), os.path.join(b, x))].append(x)
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
# Compare two files.
|
|
||||||
# Return:
|
|
||||||
# 0 for equal
|
|
||||||
# 1 for different
|
|
||||||
# 2 for funny cases (can't stat, etc.)
|
|
||||||
#
|
|
||||||
def _cmp(a, b):
|
|
||||||
try:
|
|
||||||
return not abs(cmp(a, b))
|
|
||||||
except os.error:
|
|
||||||
return 2
|
|
||||||
|
|
||||||
|
|
||||||
# Return a copy with items that occur in skip removed.
|
|
||||||
#
|
|
||||||
def _filter(list, skip):
|
|
||||||
result = []
|
|
||||||
for item in list:
|
|
||||||
if item not in skip: result.append(item)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
# Demonstration and testing.
|
|
||||||
#
|
|
||||||
def demo():
|
|
||||||
import sys
|
|
||||||
import getopt
|
|
||||||
options, args = getopt.getopt(sys.argv[1:], 'r')
|
|
||||||
if len(args) <> 2: raise getopt.error, 'need exactly two args'
|
|
||||||
dd = dircmp(args[0], args[1])
|
|
||||||
if ('-r', '') in options:
|
|
||||||
dd.report_full_closure()
|
|
||||||
else:
|
|
||||||
dd.report()
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
demo()
|
|
||||||
|
|
206
Lib/fpformat.py
206
Lib/fpformat.py
|
@ -22,120 +22,120 @@ decoder = re.compile(r'^([-+]?)0*(\d*)((?:\.\d*)?)(([eE][-+]?\d+)?)$')
|
||||||
# \4 exponent part (empty or begins with 'e' or 'E')
|
# \4 exponent part (empty or begins with 'e' or 'E')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
class NotANumber(ValueError):
|
class NotANumber(ValueError):
|
||||||
pass
|
pass
|
||||||
except TypeError:
|
except TypeError:
|
||||||
NotANumber = 'fpformat.NotANumber'
|
NotANumber = 'fpformat.NotANumber'
|
||||||
|
|
||||||
# Return (sign, intpart, fraction, expo) or raise an exception:
|
|
||||||
# sign is '+' or '-'
|
|
||||||
# intpart is 0 or more digits beginning with a nonzero
|
|
||||||
# fraction is 0 or more digits
|
|
||||||
# expo is an integer
|
|
||||||
def extract(s):
|
def extract(s):
|
||||||
res = decoder.match(s)
|
"""Return (sign, intpart, fraction, expo) or raise an exception:
|
||||||
if res is None: raise NotANumber, s
|
sign is '+' or '-'
|
||||||
sign, intpart, fraction, exppart = res.group(1,2,3,4)
|
intpart is 0 or more digits beginning with a nonzero
|
||||||
if sign == '+': sign = ''
|
fraction is 0 or more digits
|
||||||
if fraction: fraction = fraction[1:]
|
expo is an integer"""
|
||||||
if exppart: expo = int(exppart[1:])
|
res = decoder.match(s)
|
||||||
else: expo = 0
|
if res is None: raise NotANumber, s
|
||||||
return sign, intpart, fraction, expo
|
sign, intpart, fraction, exppart = res.group(1,2,3,4)
|
||||||
|
if sign == '+': sign = ''
|
||||||
|
if fraction: fraction = fraction[1:]
|
||||||
|
if exppart: expo = int(exppart[1:])
|
||||||
|
else: expo = 0
|
||||||
|
return sign, intpart, fraction, expo
|
||||||
|
|
||||||
# Remove the exponent by changing intpart and fraction
|
|
||||||
def unexpo(intpart, fraction, expo):
|
def unexpo(intpart, fraction, expo):
|
||||||
if expo > 0: # Move the point left
|
"""Remove the exponent by changing intpart and fraction."""
|
||||||
f = len(fraction)
|
if expo > 0: # Move the point left
|
||||||
intpart, fraction = intpart + fraction[:expo], fraction[expo:]
|
f = len(fraction)
|
||||||
if expo > f:
|
intpart, fraction = intpart + fraction[:expo], fraction[expo:]
|
||||||
intpart = intpart + '0'*(expo-f)
|
if expo > f:
|
||||||
elif expo < 0: # Move the point right
|
intpart = intpart + '0'*(expo-f)
|
||||||
i = len(intpart)
|
elif expo < 0: # Move the point right
|
||||||
intpart, fraction = intpart[:expo], intpart[expo:] + fraction
|
i = len(intpart)
|
||||||
if expo < -i:
|
intpart, fraction = intpart[:expo], intpart[expo:] + fraction
|
||||||
fraction = '0'*(-expo-i) + fraction
|
if expo < -i:
|
||||||
return intpart, fraction
|
fraction = '0'*(-expo-i) + fraction
|
||||||
|
return intpart, fraction
|
||||||
|
|
||||||
# Round or extend the fraction to size digs
|
|
||||||
def roundfrac(intpart, fraction, digs):
|
def roundfrac(intpart, fraction, digs):
|
||||||
f = len(fraction)
|
"""Round or extend the fraction to size digs."""
|
||||||
if f <= digs:
|
f = len(fraction)
|
||||||
return intpart, fraction + '0'*(digs-f)
|
if f <= digs:
|
||||||
i = len(intpart)
|
return intpart, fraction + '0'*(digs-f)
|
||||||
if i+digs < 0:
|
i = len(intpart)
|
||||||
return '0'*-digs, ''
|
if i+digs < 0:
|
||||||
total = intpart + fraction
|
return '0'*-digs, ''
|
||||||
nextdigit = total[i+digs]
|
total = intpart + fraction
|
||||||
if nextdigit >= '5': # Hard case: increment last digit, may have carry!
|
nextdigit = total[i+digs]
|
||||||
n = i + digs - 1
|
if nextdigit >= '5': # Hard case: increment last digit, may have carry!
|
||||||
while n >= 0:
|
n = i + digs - 1
|
||||||
if total[n] != '9': break
|
while n >= 0:
|
||||||
n = n-1
|
if total[n] != '9': break
|
||||||
else:
|
n = n-1
|
||||||
total = '0' + total
|
else:
|
||||||
i = i+1
|
total = '0' + total
|
||||||
n = 0
|
i = i+1
|
||||||
total = total[:n] + chr(ord(total[n]) + 1) + '0'*(len(total)-n-1)
|
n = 0
|
||||||
intpart, fraction = total[:i], total[i:]
|
total = total[:n] + chr(ord(total[n]) + 1) + '0'*(len(total)-n-1)
|
||||||
if digs >= 0:
|
intpart, fraction = total[:i], total[i:]
|
||||||
return intpart, fraction[:digs]
|
if digs >= 0:
|
||||||
else:
|
return intpart, fraction[:digs]
|
||||||
return intpart[:digs] + '0'*-digs, ''
|
else:
|
||||||
|
return intpart[:digs] + '0'*-digs, ''
|
||||||
|
|
||||||
# Format x as [-]ddd.ddd with 'digs' digits after the point
|
|
||||||
# and at least one digit before.
|
|
||||||
# If digs <= 0, the point is suppressed.
|
|
||||||
def fix(x, digs):
|
def fix(x, digs):
|
||||||
if type(x) != type(''): x = `x`
|
"""Format x as [-]ddd.ddd with 'digs' digits after the point
|
||||||
try:
|
and at least one digit before.
|
||||||
sign, intpart, fraction, expo = extract(x)
|
If digs <= 0, the point is suppressed."""
|
||||||
except NotANumber:
|
if type(x) != type(''): x = `x`
|
||||||
return x
|
try:
|
||||||
intpart, fraction = unexpo(intpart, fraction, expo)
|
sign, intpart, fraction, expo = extract(x)
|
||||||
intpart, fraction = roundfrac(intpart, fraction, digs)
|
except NotANumber:
|
||||||
while intpart and intpart[0] == '0': intpart = intpart[1:]
|
return x
|
||||||
if intpart == '': intpart = '0'
|
intpart, fraction = unexpo(intpart, fraction, expo)
|
||||||
if digs > 0: return sign + intpart + '.' + fraction
|
intpart, fraction = roundfrac(intpart, fraction, digs)
|
||||||
else: return sign + intpart
|
while intpart and intpart[0] == '0': intpart = intpart[1:]
|
||||||
|
if intpart == '': intpart = '0'
|
||||||
|
if digs > 0: return sign + intpart + '.' + fraction
|
||||||
|
else: return sign + intpart
|
||||||
|
|
||||||
# Format x as [-]d.dddE[+-]ddd with 'digs' digits after the point
|
|
||||||
# and exactly one digit before.
|
|
||||||
# If digs is <= 0, one digit is kept and the point is suppressed.
|
|
||||||
def sci(x, digs):
|
def sci(x, digs):
|
||||||
if type(x) != type(''): x = `x`
|
"""Format x as [-]d.dddE[+-]ddd with 'digs' digits after the point
|
||||||
sign, intpart, fraction, expo = extract(x)
|
and exactly one digit before.
|
||||||
if not intpart:
|
If digs is <= 0, one digit is kept and the point is suppressed."""
|
||||||
while fraction and fraction[0] == '0':
|
if type(x) != type(''): x = `x`
|
||||||
fraction = fraction[1:]
|
sign, intpart, fraction, expo = extract(x)
|
||||||
expo = expo - 1
|
if not intpart:
|
||||||
if fraction:
|
while fraction and fraction[0] == '0':
|
||||||
intpart, fraction = fraction[0], fraction[1:]
|
fraction = fraction[1:]
|
||||||
expo = expo - 1
|
expo = expo - 1
|
||||||
else:
|
if fraction:
|
||||||
intpart = '0'
|
intpart, fraction = fraction[0], fraction[1:]
|
||||||
else:
|
expo = expo - 1
|
||||||
expo = expo + len(intpart) - 1
|
else:
|
||||||
intpart, fraction = intpart[0], intpart[1:] + fraction
|
intpart = '0'
|
||||||
digs = max(0, digs)
|
else:
|
||||||
intpart, fraction = roundfrac(intpart, fraction, digs)
|
expo = expo + len(intpart) - 1
|
||||||
if len(intpart) > 1:
|
intpart, fraction = intpart[0], intpart[1:] + fraction
|
||||||
intpart, fraction, expo = \
|
digs = max(0, digs)
|
||||||
intpart[0], intpart[1:] + fraction[:-1], \
|
intpart, fraction = roundfrac(intpart, fraction, digs)
|
||||||
expo + len(intpart) - 1
|
if len(intpart) > 1:
|
||||||
s = sign + intpart
|
intpart, fraction, expo = \
|
||||||
if digs > 0: s = s + '.' + fraction
|
intpart[0], intpart[1:] + fraction[:-1], \
|
||||||
e = `abs(expo)`
|
expo + len(intpart) - 1
|
||||||
e = '0'*(3-len(e)) + e
|
s = sign + intpart
|
||||||
if expo < 0: e = '-' + e
|
if digs > 0: s = s + '.' + fraction
|
||||||
else: e = '+' + e
|
e = `abs(expo)`
|
||||||
return s + 'e' + e
|
e = '0'*(3-len(e)) + e
|
||||||
|
if expo < 0: e = '-' + e
|
||||||
|
else: e = '+' + e
|
||||||
|
return s + 'e' + e
|
||||||
|
|
||||||
# Interactive test run
|
|
||||||
def test():
|
def test():
|
||||||
try:
|
"""Interactive test run."""
|
||||||
while 1:
|
try:
|
||||||
x, digs = input('Enter (x, digs): ')
|
while 1:
|
||||||
print x, fix(x, digs), sci(x, digs)
|
x, digs = input('Enter (x, digs): ')
|
||||||
except (EOFError, KeyboardInterrupt):
|
print x, fix(x, digs), sci(x, digs)
|
||||||
pass
|
except (EOFError, KeyboardInterrupt):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
280
Lib/gopherlib.py
280
Lib/gopherlib.py
|
@ -1,4 +1,4 @@
|
||||||
# Gopher protocol client interface
|
"""Gopher protocol client interface."""
|
||||||
|
|
||||||
import string
|
import string
|
||||||
|
|
||||||
|
@ -29,180 +29,180 @@ A_IMAGE = 'I'
|
||||||
A_WHOIS = 'w'
|
A_WHOIS = 'w'
|
||||||
A_QUERY = 'q'
|
A_QUERY = 'q'
|
||||||
A_GIF = 'g'
|
A_GIF = 'g'
|
||||||
A_HTML = 'h' # HTML file
|
A_HTML = 'h' # HTML file
|
||||||
A_WWW = 'w' # WWW address
|
A_WWW = 'w' # WWW address
|
||||||
A_PLUS_IMAGE = ':'
|
A_PLUS_IMAGE = ':'
|
||||||
A_PLUS_MOVIE = ';'
|
A_PLUS_MOVIE = ';'
|
||||||
A_PLUS_SOUND = '<'
|
A_PLUS_SOUND = '<'
|
||||||
|
|
||||||
|
|
||||||
# Function mapping all file types to strings; unknown types become TYPE='x'
|
|
||||||
_names = dir()
|
_names = dir()
|
||||||
_type_to_name_map = {}
|
_type_to_name_map = {}
|
||||||
def type_to_name(gtype):
|
def type_to_name(gtype):
|
||||||
global _type_to_name_map
|
"""Map all file types to strings; unknown types become TYPE='x'."""
|
||||||
if _type_to_name_map=={}:
|
global _type_to_name_map
|
||||||
for name in _names:
|
if _type_to_name_map=={}:
|
||||||
if name[:2] == 'A_':
|
for name in _names:
|
||||||
_type_to_name_map[eval(name)] = name[2:]
|
if name[:2] == 'A_':
|
||||||
if _type_to_name_map.has_key(gtype):
|
_type_to_name_map[eval(name)] = name[2:]
|
||||||
return _type_to_name_map[gtype]
|
if _type_to_name_map.has_key(gtype):
|
||||||
return 'TYPE=' + `gtype`
|
return _type_to_name_map[gtype]
|
||||||
|
return 'TYPE=' + `gtype`
|
||||||
|
|
||||||
# Names for characters and strings
|
# Names for characters and strings
|
||||||
CRLF = '\r\n'
|
CRLF = '\r\n'
|
||||||
TAB = '\t'
|
TAB = '\t'
|
||||||
|
|
||||||
# Send a selector to a given host and port, return a file with the reply
|
|
||||||
def send_selector(selector, host, port = 0):
|
def send_selector(selector, host, port = 0):
|
||||||
import socket
|
"""Send a selector to a given host and port, return a file with the reply."""
|
||||||
import string
|
import socket
|
||||||
if not port:
|
import string
|
||||||
i = string.find(host, ':')
|
if not port:
|
||||||
if i >= 0:
|
i = string.find(host, ':')
|
||||||
host, port = host[:i], string.atoi(host[i+1:])
|
if i >= 0:
|
||||||
if not port:
|
host, port = host[:i], string.atoi(host[i+1:])
|
||||||
port = DEF_PORT
|
if not port:
|
||||||
elif type(port) == type(''):
|
port = DEF_PORT
|
||||||
port = string.atoi(port)
|
elif type(port) == type(''):
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
port = string.atoi(port)
|
||||||
s.connect(host, port)
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
s.send(selector + CRLF)
|
s.connect(host, port)
|
||||||
s.shutdown(1)
|
s.send(selector + CRLF)
|
||||||
return s.makefile('rb')
|
s.shutdown(1)
|
||||||
|
return s.makefile('rb')
|
||||||
|
|
||||||
# Send a selector and a query string
|
|
||||||
def send_query(selector, query, host, port = 0):
|
def send_query(selector, query, host, port = 0):
|
||||||
return send_selector(selector + '\t' + query, host, port)
|
"""Send a selector and a query string."""
|
||||||
|
return send_selector(selector + '\t' + query, host, port)
|
||||||
|
|
||||||
# Takes a path as returned by urlparse and returns the appropriate selector
|
|
||||||
def path_to_selector(path):
|
def path_to_selector(path):
|
||||||
if path=="/":
|
"""Takes a path as returned by urlparse and returns the appropriate selector."""
|
||||||
return "/"
|
if path=="/":
|
||||||
else:
|
return "/"
|
||||||
return path[2:] # Cuts initial slash and data type identifier
|
else:
|
||||||
|
return path[2:] # Cuts initial slash and data type identifier
|
||||||
|
|
||||||
# Takes a path as returned by urlparse and maps it to a string
|
|
||||||
# See section 3.4 of RFC 1738 for details
|
|
||||||
def path_to_datatype_name(path):
|
def path_to_datatype_name(path):
|
||||||
if path=="/":
|
"""Takes a path as returned by urlparse and maps it to a string.
|
||||||
# No way to tell, although "INDEX" is likely
|
See section 3.4 of RFC 1738 for details."""
|
||||||
return "TYPE='unknown'"
|
if path=="/":
|
||||||
else:
|
# No way to tell, although "INDEX" is likely
|
||||||
return type_to_name(path[1])
|
return "TYPE='unknown'"
|
||||||
|
else:
|
||||||
|
return type_to_name(path[1])
|
||||||
|
|
||||||
# The following functions interpret the data returned by the gopher
|
# The following functions interpret the data returned by the gopher
|
||||||
# server according to the expected type, e.g. textfile or directory
|
# server according to the expected type, e.g. textfile or directory
|
||||||
|
|
||||||
# Get a directory in the form of a list of entries
|
|
||||||
def get_directory(f):
|
def get_directory(f):
|
||||||
import string
|
"""Get a directory in the form of a list of entries."""
|
||||||
list = []
|
import string
|
||||||
while 1:
|
list = []
|
||||||
line = f.readline()
|
while 1:
|
||||||
if not line:
|
line = f.readline()
|
||||||
print '(Unexpected EOF from server)'
|
if not line:
|
||||||
break
|
print '(Unexpected EOF from server)'
|
||||||
if line[-2:] == CRLF:
|
break
|
||||||
line = line[:-2]
|
if line[-2:] == CRLF:
|
||||||
elif line[-1:] in CRLF:
|
line = line[:-2]
|
||||||
line = line[:-1]
|
elif line[-1:] in CRLF:
|
||||||
if line == '.':
|
line = line[:-1]
|
||||||
break
|
if line == '.':
|
||||||
if not line:
|
break
|
||||||
print '(Empty line from server)'
|
if not line:
|
||||||
continue
|
print '(Empty line from server)'
|
||||||
gtype = line[0]
|
continue
|
||||||
parts = string.splitfields(line[1:], TAB)
|
gtype = line[0]
|
||||||
if len(parts) < 4:
|
parts = string.splitfields(line[1:], TAB)
|
||||||
print '(Bad line from server:', `line`, ')'
|
if len(parts) < 4:
|
||||||
continue
|
print '(Bad line from server:', `line`, ')'
|
||||||
if len(parts) > 4:
|
continue
|
||||||
if parts[4:] != ['+']:
|
if len(parts) > 4:
|
||||||
print '(Extra info from server:',
|
if parts[4:] != ['+']:
|
||||||
print parts[4:], ')'
|
print '(Extra info from server:',
|
||||||
else:
|
print parts[4:], ')'
|
||||||
parts.append('')
|
else:
|
||||||
parts.insert(0, gtype)
|
parts.append('')
|
||||||
list.append(parts)
|
parts.insert(0, gtype)
|
||||||
return list
|
list.append(parts)
|
||||||
|
return list
|
||||||
|
|
||||||
# Get a text file as a list of lines, with trailing CRLF stripped
|
|
||||||
def get_textfile(f):
|
def get_textfile(f):
|
||||||
list = []
|
"""Get a text file as a list of lines, with trailing CRLF stripped."""
|
||||||
get_alt_textfile(f, list.append)
|
list = []
|
||||||
return list
|
get_alt_textfile(f, list.append)
|
||||||
|
return list
|
||||||
|
|
||||||
# Get a text file and pass each line to a function, with trailing CRLF stripped
|
|
||||||
def get_alt_textfile(f, func):
|
def get_alt_textfile(f, func):
|
||||||
while 1:
|
"""Get a text file and pass each line to a function, with trailing CRLF stripped."""
|
||||||
line = f.readline()
|
while 1:
|
||||||
if not line:
|
line = f.readline()
|
||||||
print '(Unexpected EOF from server)'
|
if not line:
|
||||||
break
|
print '(Unexpected EOF from server)'
|
||||||
if line[-2:] == CRLF:
|
break
|
||||||
line = line[:-2]
|
if line[-2:] == CRLF:
|
||||||
elif line[-1:] in CRLF:
|
line = line[:-2]
|
||||||
line = line[:-1]
|
elif line[-1:] in CRLF:
|
||||||
if line == '.':
|
line = line[:-1]
|
||||||
break
|
if line == '.':
|
||||||
if line[:2] == '..':
|
break
|
||||||
line = line[1:]
|
if line[:2] == '..':
|
||||||
func(line)
|
line = line[1:]
|
||||||
|
func(line)
|
||||||
|
|
||||||
# Get a binary file as one solid data block
|
|
||||||
def get_binary(f):
|
def get_binary(f):
|
||||||
data = f.read()
|
"""Get a binary file as one solid data block."""
|
||||||
return data
|
data = f.read()
|
||||||
|
return data
|
||||||
|
|
||||||
# Get a binary file and pass each block to a function
|
|
||||||
def get_alt_binary(f, func, blocksize):
|
def get_alt_binary(f, func, blocksize):
|
||||||
while 1:
|
"""Get a binary file and pass each block to a function."""
|
||||||
data = f.read(blocksize)
|
while 1:
|
||||||
if not data:
|
data = f.read(blocksize)
|
||||||
break
|
if not data:
|
||||||
func(data)
|
break
|
||||||
|
func(data)
|
||||||
|
|
||||||
# Trivial test program
|
|
||||||
def test():
|
def test():
|
||||||
import sys
|
"""Trivial test program."""
|
||||||
import getopt
|
import sys
|
||||||
opts, args = getopt.getopt(sys.argv[1:], '')
|
import getopt
|
||||||
selector = DEF_SELECTOR
|
opts, args = getopt.getopt(sys.argv[1:], '')
|
||||||
type = selector[0]
|
selector = DEF_SELECTOR
|
||||||
host = DEF_HOST
|
type = selector[0]
|
||||||
port = DEF_PORT
|
host = DEF_HOST
|
||||||
if args:
|
port = DEF_PORT
|
||||||
host = args[0]
|
if args:
|
||||||
args = args[1:]
|
host = args[0]
|
||||||
if args:
|
args = args[1:]
|
||||||
type = args[0]
|
if args:
|
||||||
args = args[1:]
|
type = args[0]
|
||||||
if len(type) > 1:
|
args = args[1:]
|
||||||
type, selector = type[0], type
|
if len(type) > 1:
|
||||||
else:
|
type, selector = type[0], type
|
||||||
selector = ''
|
else:
|
||||||
if args:
|
selector = ''
|
||||||
selector = args[0]
|
if args:
|
||||||
args = args[1:]
|
selector = args[0]
|
||||||
query = ''
|
args = args[1:]
|
||||||
if args:
|
query = ''
|
||||||
query = args[0]
|
if args:
|
||||||
args = args[1:]
|
query = args[0]
|
||||||
if type == A_INDEX:
|
args = args[1:]
|
||||||
f = send_query(selector, query, host)
|
if type == A_INDEX:
|
||||||
else:
|
f = send_query(selector, query, host)
|
||||||
f = send_selector(selector, host)
|
else:
|
||||||
if type == A_TEXT:
|
f = send_selector(selector, host)
|
||||||
list = get_textfile(f)
|
if type == A_TEXT:
|
||||||
for item in list: print item
|
list = get_textfile(f)
|
||||||
elif type in (A_MENU, A_INDEX):
|
for item in list: print item
|
||||||
list = get_directory(f)
|
elif type in (A_MENU, A_INDEX):
|
||||||
for item in list: print item
|
list = get_directory(f)
|
||||||
else:
|
for item in list: print item
|
||||||
data = get_binary(f)
|
else:
|
||||||
print 'binary data:', len(data), 'bytes:', `data[:100]`[:40]
|
data = get_binary(f)
|
||||||
|
print 'binary data:', len(data), 'bytes:', `data[:100]`[:40]
|
||||||
|
|
||||||
# Run the test when run as script
|
# Run the test when run as script
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
test()
|
test()
|
||||||
|
|
12
Lib/gzip.py
12
Lib/gzip.py
|
@ -1,15 +1,15 @@
|
||||||
|
"""This module implements a function that reads and writes a gzipped file.
|
||||||
|
The user of the file doesn't have to worry about the compression,
|
||||||
|
but random access is not allowed."""
|
||||||
|
|
||||||
|
# based on Andrew Kuchling's minigzip.py distributed with the zlib module
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import string
|
import string
|
||||||
import zlib
|
import zlib
|
||||||
import struct
|
import struct
|
||||||
import __builtin__
|
import __builtin__
|
||||||
|
|
||||||
# implements a python function that reads and writes a gzipped file
|
|
||||||
# the user of the file doesn't have to worry about the compression,
|
|
||||||
# but random access is not allowed
|
|
||||||
|
|
||||||
# based on Andrew Kuchling's minigzip.py distributed with the zlib module
|
|
||||||
|
|
||||||
FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT = 1, 2, 4, 8, 16
|
FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT = 1, 2, 4, 8, 16
|
||||||
|
|
||||||
READ, WRITE = 1, 2
|
READ, WRITE = 1, 2
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
"""HTML character entity references."""
|
||||||
|
|
||||||
entitydefs = {
|
entitydefs = {
|
||||||
'AElig': '\306', # latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1
|
'AElig': '\306', # latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1
|
||||||
'Aacute': '\301', # latin capital letter A with acute, U+00C1 ISOlat1
|
'Aacute': '\301', # latin capital letter A with acute, U+00C1 ISOlat1
|
||||||
|
|
176
Lib/imghdr.py
176
Lib/imghdr.py
|
@ -1,4 +1,4 @@
|
||||||
# Recognizing image files based on their first few bytes.
|
"""Recognize image file formats based on their first few bytes."""
|
||||||
|
|
||||||
|
|
||||||
#-------------------------#
|
#-------------------------#
|
||||||
|
@ -6,25 +6,25 @@
|
||||||
#-------------------------#
|
#-------------------------#
|
||||||
|
|
||||||
def what(file, h=None):
|
def what(file, h=None):
|
||||||
if h is None:
|
if h is None:
|
||||||
if type(file) == type(''):
|
if type(file) == type(''):
|
||||||
f = open(file, 'rb')
|
f = open(file, 'rb')
|
||||||
h = f.read(32)
|
h = f.read(32)
|
||||||
else:
|
else:
|
||||||
location = file.tell()
|
location = file.tell()
|
||||||
h = file.read(32)
|
h = file.read(32)
|
||||||
file.seek(location)
|
file.seek(location)
|
||||||
f = None
|
f = None
|
||||||
else:
|
else:
|
||||||
f = None
|
f = None
|
||||||
try:
|
try:
|
||||||
for tf in tests:
|
for tf in tests:
|
||||||
res = tf(h, f)
|
res = tf(h, f)
|
||||||
if res:
|
if res:
|
||||||
return res
|
return res
|
||||||
finally:
|
finally:
|
||||||
if f: f.close()
|
if f: f.close()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
#---------------------------------#
|
#---------------------------------#
|
||||||
|
@ -34,81 +34,81 @@ def what(file, h=None):
|
||||||
tests = []
|
tests = []
|
||||||
|
|
||||||
def test_rgb(h, f):
|
def test_rgb(h, f):
|
||||||
# SGI image library
|
"""SGI image library"""
|
||||||
if h[:2] == '\001\332':
|
if h[:2] == '\001\332':
|
||||||
return 'rgb'
|
return 'rgb'
|
||||||
|
|
||||||
tests.append(test_rgb)
|
tests.append(test_rgb)
|
||||||
|
|
||||||
def test_gif(h, f):
|
def test_gif(h, f):
|
||||||
# GIF ('87 and '89 variants)
|
"""GIF ('87 and '89 variants)"""
|
||||||
if h[:6] in ('GIF87a', 'GIF89a'):
|
if h[:6] in ('GIF87a', 'GIF89a'):
|
||||||
return 'gif'
|
return 'gif'
|
||||||
|
|
||||||
tests.append(test_gif)
|
tests.append(test_gif)
|
||||||
|
|
||||||
def test_pbm(h, f):
|
def test_pbm(h, f):
|
||||||
# PBM (portable bitmap)
|
"""PBM (portable bitmap)"""
|
||||||
if len(h) >= 3 and \
|
if len(h) >= 3 and \
|
||||||
h[0] == 'P' and h[1] in '14' and h[2] in ' \t\n\r':
|
h[0] == 'P' and h[1] in '14' and h[2] in ' \t\n\r':
|
||||||
return 'pbm'
|
return 'pbm'
|
||||||
|
|
||||||
tests.append(test_pbm)
|
tests.append(test_pbm)
|
||||||
|
|
||||||
def test_pgm(h, f):
|
def test_pgm(h, f):
|
||||||
# PGM (portable graymap)
|
"""PGM (portable graymap)"""
|
||||||
if len(h) >= 3 and \
|
if len(h) >= 3 and \
|
||||||
h[0] == 'P' and h[1] in '25' and h[2] in ' \t\n\r':
|
h[0] == 'P' and h[1] in '25' and h[2] in ' \t\n\r':
|
||||||
return 'pgm'
|
return 'pgm'
|
||||||
|
|
||||||
tests.append(test_pgm)
|
tests.append(test_pgm)
|
||||||
|
|
||||||
def test_ppm(h, f):
|
def test_ppm(h, f):
|
||||||
# PPM (portable pixmap)
|
"""PPM (portable pixmap)"""
|
||||||
if len(h) >= 3 and \
|
if len(h) >= 3 and \
|
||||||
h[0] == 'P' and h[1] in '36' and h[2] in ' \t\n\r':
|
h[0] == 'P' and h[1] in '36' and h[2] in ' \t\n\r':
|
||||||
return 'ppm'
|
return 'ppm'
|
||||||
|
|
||||||
tests.append(test_ppm)
|
tests.append(test_ppm)
|
||||||
|
|
||||||
def test_tiff(h, f):
|
def test_tiff(h, f):
|
||||||
# TIFF (can be in Motorola or Intel byte order)
|
"""TIFF (can be in Motorola or Intel byte order)"""
|
||||||
if h[:2] in ('MM', 'II'):
|
if h[:2] in ('MM', 'II'):
|
||||||
return 'tiff'
|
return 'tiff'
|
||||||
|
|
||||||
tests.append(test_tiff)
|
tests.append(test_tiff)
|
||||||
|
|
||||||
def test_rast(h, f):
|
def test_rast(h, f):
|
||||||
# Sun raster file
|
"""Sun raster file"""
|
||||||
if h[:4] == '\x59\xA6\x6A\x95':
|
if h[:4] == '\x59\xA6\x6A\x95':
|
||||||
return 'rast'
|
return 'rast'
|
||||||
|
|
||||||
tests.append(test_rast)
|
tests.append(test_rast)
|
||||||
|
|
||||||
def test_xbm(h, f):
|
def test_xbm(h, f):
|
||||||
# X bitmap (X10 or X11)
|
"""X bitmap (X10 or X11)"""
|
||||||
s = '#define '
|
s = '#define '
|
||||||
if h[:len(s)] == s:
|
if h[:len(s)] == s:
|
||||||
return 'xbm'
|
return 'xbm'
|
||||||
|
|
||||||
tests.append(test_xbm)
|
tests.append(test_xbm)
|
||||||
|
|
||||||
def test_jpeg(h, f):
|
def test_jpeg(h, f):
|
||||||
# JPEG data in JFIF format
|
"""JPEG data in JFIF format"""
|
||||||
if h[6:10] == 'JFIF':
|
if h[6:10] == 'JFIF':
|
||||||
return 'jpeg'
|
return 'jpeg'
|
||||||
|
|
||||||
tests.append(test_jpeg)
|
tests.append(test_jpeg)
|
||||||
|
|
||||||
def test_bmp(h, f):
|
def test_bmp(h, f):
|
||||||
if h[:2] == 'BM':
|
if h[:2] == 'BM':
|
||||||
return 'bmp'
|
return 'bmp'
|
||||||
|
|
||||||
tests.append(test_bmp)
|
tests.append(test_bmp)
|
||||||
|
|
||||||
def test_png(h, f):
|
def test_png(h, f):
|
||||||
if h[:8] == "\211PNG\r\n\032\n":
|
if h[:8] == "\211PNG\r\n\032\n":
|
||||||
return 'png'
|
return 'png'
|
||||||
|
|
||||||
tests.append(test_png)
|
tests.append(test_png)
|
||||||
|
|
||||||
|
@ -117,37 +117,37 @@ tests.append(test_png)
|
||||||
#--------------------#
|
#--------------------#
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
import sys
|
import sys
|
||||||
recursive = 0
|
recursive = 0
|
||||||
if sys.argv[1:] and sys.argv[1] == '-r':
|
if sys.argv[1:] and sys.argv[1] == '-r':
|
||||||
del sys.argv[1:2]
|
del sys.argv[1:2]
|
||||||
recursive = 1
|
recursive = 1
|
||||||
try:
|
try:
|
||||||
if sys.argv[1:]:
|
if sys.argv[1:]:
|
||||||
testall(sys.argv[1:], recursive, 1)
|
testall(sys.argv[1:], recursive, 1)
|
||||||
else:
|
else:
|
||||||
testall(['.'], recursive, 1)
|
testall(['.'], recursive, 1)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
sys.stderr.write('\n[Interrupted]\n')
|
sys.stderr.write('\n[Interrupted]\n')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def testall(list, recursive, toplevel):
|
def testall(list, recursive, toplevel):
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
for filename in list:
|
for filename in list:
|
||||||
if os.path.isdir(filename):
|
if os.path.isdir(filename):
|
||||||
print filename + '/:',
|
print filename + '/:',
|
||||||
if recursive or toplevel:
|
if recursive or toplevel:
|
||||||
print 'recursing down:'
|
print 'recursing down:'
|
||||||
import glob
|
import glob
|
||||||
names = glob.glob(os.path.join(filename, '*'))
|
names = glob.glob(os.path.join(filename, '*'))
|
||||||
testall(names, recursive, 0)
|
testall(names, recursive, 0)
|
||||||
else:
|
else:
|
||||||
print '*** directory (use -r) ***'
|
print '*** directory (use -r) ***'
|
||||||
else:
|
else:
|
||||||
print filename + ':',
|
print filename + ':',
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
try:
|
try:
|
||||||
print what(filename)
|
print what(filename)
|
||||||
except IOError:
|
except IOError:
|
||||||
print '*** not found ***'
|
print '*** not found ***'
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
#! /usr/bin/env python
|
#! /usr/bin/env python
|
||||||
#
|
|
||||||
# Keywords (from "graminit.c")
|
"""Keywords (from "graminit.c")
|
||||||
#
|
|
||||||
# This file is automatically generated; please don't muck it up!
|
This file is automatically generated; please don't muck it up!
|
||||||
#
|
|
||||||
# To update the symbols in this file, 'cd' to the top directory of
|
To update the symbols in this file, 'cd' to the top directory of
|
||||||
# the python source tree after building the interpreter and run:
|
the python source tree after building the interpreter and run:
|
||||||
#
|
|
||||||
# python Lib/keyword.py
|
python Lib/keyword.py
|
||||||
|
"""
|
||||||
|
|
||||||
kwlist = [
|
kwlist = [
|
||||||
#--start keywords--
|
#--start keywords--
|
||||||
|
|
130
Lib/linecache.py
130
Lib/linecache.py
|
@ -1,18 +1,20 @@
|
||||||
# Cache lines from files.
|
"""Cache lines from files.
|
||||||
# This is intended to read lines from modules imported -- hence if a filename
|
|
||||||
# is not found, it will look down the module search path for a file by
|
This is intended to read lines from modules imported -- hence if a filename
|
||||||
# that name.
|
is not found, it will look down the module search path for a file by
|
||||||
|
that name.
|
||||||
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from stat import *
|
from stat import *
|
||||||
|
|
||||||
def getline(filename, lineno):
|
def getline(filename, lineno):
|
||||||
lines = getlines(filename)
|
lines = getlines(filename)
|
||||||
if 1 <= lineno <= len(lines):
|
if 1 <= lineno <= len(lines):
|
||||||
return lines[lineno-1]
|
return lines[lineno-1]
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
# The cache
|
# The cache
|
||||||
|
@ -20,71 +22,71 @@ def getline(filename, lineno):
|
||||||
cache = {} # The cache
|
cache = {} # The cache
|
||||||
|
|
||||||
|
|
||||||
# Clear the cache entirely
|
|
||||||
|
|
||||||
def clearcache():
|
def clearcache():
|
||||||
global cache
|
"""Clear the cache entirely."""
|
||||||
cache = {}
|
|
||||||
|
|
||||||
|
global cache
|
||||||
|
cache = {}
|
||||||
|
|
||||||
# Get the lines for a file from the cache.
|
|
||||||
# Update the cache if it doesn't contain an entry for this file already.
|
|
||||||
|
|
||||||
def getlines(filename):
|
def getlines(filename):
|
||||||
if cache.has_key(filename):
|
"""Get the lines for a file from the cache.
|
||||||
return cache[filename][2]
|
Update the cache if it doesn't contain an entry for this file already."""
|
||||||
else:
|
|
||||||
return updatecache(filename)
|
|
||||||
|
|
||||||
|
if cache.has_key(filename):
|
||||||
|
return cache[filename][2]
|
||||||
|
else:
|
||||||
|
return updatecache(filename)
|
||||||
|
|
||||||
# Discard cache entries that are out of date.
|
|
||||||
# (This is not checked upon each call!)
|
|
||||||
|
|
||||||
def checkcache():
|
def checkcache():
|
||||||
for filename in cache.keys():
|
"""Discard cache entries that are out of date.
|
||||||
size, mtime, lines, fullname = cache[filename]
|
(This is not checked upon each call!)"""
|
||||||
try:
|
|
||||||
stat = os.stat(fullname)
|
|
||||||
except os.error:
|
|
||||||
del cache[filename]
|
|
||||||
continue
|
|
||||||
if size <> stat[ST_SIZE] or mtime <> stat[ST_MTIME]:
|
|
||||||
del cache[filename]
|
|
||||||
|
|
||||||
|
for filename in cache.keys():
|
||||||
|
size, mtime, lines, fullname = cache[filename]
|
||||||
|
try:
|
||||||
|
stat = os.stat(fullname)
|
||||||
|
except os.error:
|
||||||
|
del cache[filename]
|
||||||
|
continue
|
||||||
|
if size <> stat[ST_SIZE] or mtime <> stat[ST_MTIME]:
|
||||||
|
del cache[filename]
|
||||||
|
|
||||||
# Update a cache entry and return its list of lines.
|
|
||||||
# If something's wrong, print a message, discard the cache entry,
|
|
||||||
# and return an empty list.
|
|
||||||
|
|
||||||
def updatecache(filename):
|
def updatecache(filename):
|
||||||
if cache.has_key(filename):
|
"""Update a cache entry and return its list of lines.
|
||||||
del cache[filename]
|
If something's wrong, print a message, discard the cache entry,
|
||||||
if not filename or filename[0] + filename[-1] == '<>':
|
and return an empty list."""
|
||||||
return []
|
|
||||||
fullname = filename
|
if cache.has_key(filename):
|
||||||
try:
|
del cache[filename]
|
||||||
stat = os.stat(fullname)
|
if not filename or filename[0] + filename[-1] == '<>':
|
||||||
except os.error, msg:
|
return []
|
||||||
# Try looking through the module search path
|
fullname = filename
|
||||||
basename = os.path.split(filename)[1]
|
try:
|
||||||
for dirname in sys.path:
|
stat = os.stat(fullname)
|
||||||
fullname = os.path.join(dirname, basename)
|
except os.error, msg:
|
||||||
try:
|
# Try looking through the module search path
|
||||||
stat = os.stat(fullname)
|
basename = os.path.split(filename)[1]
|
||||||
break
|
for dirname in sys.path:
|
||||||
except os.error:
|
fullname = os.path.join(dirname, basename)
|
||||||
pass
|
try:
|
||||||
else:
|
stat = os.stat(fullname)
|
||||||
# No luck
|
break
|
||||||
## print '*** Cannot stat', filename, ':', msg
|
except os.error:
|
||||||
return []
|
pass
|
||||||
try:
|
else:
|
||||||
fp = open(fullname, 'r')
|
# No luck
|
||||||
lines = fp.readlines()
|
## print '*** Cannot stat', filename, ':', msg
|
||||||
fp.close()
|
return []
|
||||||
except IOError, msg:
|
try:
|
||||||
## print '*** Cannot open', fullname, ':', msg
|
fp = open(fullname, 'r')
|
||||||
return []
|
lines = fp.readlines()
|
||||||
size, mtime = stat[ST_SIZE], stat[ST_MTIME]
|
fp.close()
|
||||||
cache[filename] = size, mtime, lines, fullname
|
except IOError, msg:
|
||||||
return lines
|
## print '*** Cannot open', fullname, ':', msg
|
||||||
|
return []
|
||||||
|
size, mtime = stat[ST_SIZE], stat[ST_MTIME]
|
||||||
|
cache[filename] = size, mtime, lines, fullname
|
||||||
|
return lines
|
||||||
|
|
276
Lib/macpath.py
276
Lib/macpath.py
|
@ -1,4 +1,4 @@
|
||||||
# module 'macpath' -- pathname (or -related) operations for the Macintosh
|
"""Pathname and path-related operations for the Macintosh."""
|
||||||
|
|
||||||
import string
|
import string
|
||||||
import os
|
import os
|
||||||
|
@ -10,77 +10,77 @@ from stat import *
|
||||||
normcase = string.lower
|
normcase = string.lower
|
||||||
|
|
||||||
|
|
||||||
# Return true if a path is absolute.
|
|
||||||
# On the Mac, relative paths begin with a colon,
|
|
||||||
# but as a special case, paths with no colons at all are also relative.
|
|
||||||
# Anything else is absolute (the string up to the first colon is the
|
|
||||||
# volume name).
|
|
||||||
|
|
||||||
def isabs(s):
|
def isabs(s):
|
||||||
return ':' in s and s[0] <> ':'
|
"""Return true if a path is absolute.
|
||||||
|
On the Mac, relative paths begin with a colon,
|
||||||
|
but as a special case, paths with no colons at all are also relative.
|
||||||
|
Anything else is absolute (the string up to the first colon is the
|
||||||
|
volume name)."""
|
||||||
|
|
||||||
|
return ':' in s and s[0] <> ':'
|
||||||
|
|
||||||
|
|
||||||
def join(s, *p):
|
def join(s, *p):
|
||||||
path = s
|
path = s
|
||||||
for t in p:
|
for t in p:
|
||||||
if (not s) or isabs(t):
|
if (not s) or isabs(t):
|
||||||
path = t
|
path = t
|
||||||
continue
|
continue
|
||||||
if t[:1] == ':':
|
if t[:1] == ':':
|
||||||
t = t[1:]
|
t = t[1:]
|
||||||
if ':' not in path:
|
if ':' not in path:
|
||||||
path = ':' + path
|
path = ':' + path
|
||||||
if path[-1:] <> ':':
|
if path[-1:] <> ':':
|
||||||
path = path + ':'
|
path = path + ':'
|
||||||
path = path + t
|
path = path + t
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
# Split a pathname in two parts: the directory leading up to the final bit,
|
|
||||||
# and the basename (the filename, without colons, in that directory).
|
|
||||||
# The result (s, t) is such that join(s, t) yields the original argument.
|
|
||||||
|
|
||||||
def split(s):
|
def split(s):
|
||||||
if ':' not in s: return '', s
|
"""Split a pathname into two parts: the directory leading up to the final
|
||||||
colon = 0
|
bit, and the basename (the filename, without colons, in that directory).
|
||||||
for i in range(len(s)):
|
The result (s, t) is such that join(s, t) yields the original argument."""
|
||||||
if s[i] == ':': colon = i+1
|
|
||||||
path, file = s[:colon-1], s[colon:]
|
|
||||||
if path and not ':' in path:
|
|
||||||
path = path + ':'
|
|
||||||
return path, file
|
|
||||||
|
|
||||||
|
if ':' not in s: return '', s
|
||||||
|
colon = 0
|
||||||
|
for i in range(len(s)):
|
||||||
|
if s[i] == ':': colon = i+1
|
||||||
|
path, file = s[:colon-1], s[colon:]
|
||||||
|
if path and not ':' in path:
|
||||||
|
path = path + ':'
|
||||||
|
return path, file
|
||||||
|
|
||||||
# Split a path in root and extension.
|
|
||||||
# The extension is everything starting at the last dot in the last
|
|
||||||
# pathname component; the root is everything before that.
|
|
||||||
# It is always true that root + ext == p.
|
|
||||||
|
|
||||||
def splitext(p):
|
def splitext(p):
|
||||||
root, ext = '', ''
|
"""Split a path into root and extension.
|
||||||
for c in p:
|
The extension is everything starting at the last dot in the last
|
||||||
if c == ':':
|
pathname component; the root is everything before that.
|
||||||
root, ext = root + ext + c, ''
|
It is always true that root + ext == p."""
|
||||||
elif c == '.':
|
|
||||||
if ext:
|
|
||||||
root, ext = root + ext, c
|
|
||||||
else:
|
|
||||||
ext = c
|
|
||||||
elif ext:
|
|
||||||
ext = ext + c
|
|
||||||
else:
|
|
||||||
root = root + c
|
|
||||||
return root, ext
|
|
||||||
|
|
||||||
|
root, ext = '', ''
|
||||||
|
for c in p:
|
||||||
|
if c == ':':
|
||||||
|
root, ext = root + ext + c, ''
|
||||||
|
elif c == '.':
|
||||||
|
if ext:
|
||||||
|
root, ext = root + ext, c
|
||||||
|
else:
|
||||||
|
ext = c
|
||||||
|
elif ext:
|
||||||
|
ext = ext + c
|
||||||
|
else:
|
||||||
|
root = root + c
|
||||||
|
return root, ext
|
||||||
|
|
||||||
# Split a pathname into a drive specification and the rest of the
|
|
||||||
# path. Useful on DOS/Windows/NT; on the Mac, the drive is always
|
|
||||||
# empty (don't use the volume name -- it doesn't have the same
|
|
||||||
# syntactic and semantic oddities as DOS drive letters, such as there
|
|
||||||
# being a separate current directory per drive).
|
|
||||||
|
|
||||||
def splitdrive(p):
|
def splitdrive(p):
|
||||||
return '', p
|
"""Split a pathname into a drive specification and the rest of the
|
||||||
|
path. Useful on DOS/Windows/NT; on the Mac, the drive is always
|
||||||
|
empty (don't use the volume name -- it doesn't have the same
|
||||||
|
syntactic and semantic oddities as DOS drive letters, such as there
|
||||||
|
being a separate current directory per drive)."""
|
||||||
|
|
||||||
|
return '', p
|
||||||
|
|
||||||
|
|
||||||
# Short interfaces to split()
|
# Short interfaces to split()
|
||||||
|
@ -89,14 +89,14 @@ def dirname(s): return split(s)[0]
|
||||||
def basename(s): return split(s)[1]
|
def basename(s): return split(s)[1]
|
||||||
|
|
||||||
|
|
||||||
# Return true if the pathname refers to an existing directory.
|
|
||||||
|
|
||||||
def isdir(s):
|
def isdir(s):
|
||||||
try:
|
"""Return true if the pathname refers to an existing directory."""
|
||||||
st = os.stat(s)
|
|
||||||
except os.error:
|
try:
|
||||||
return 0
|
st = os.stat(s)
|
||||||
return S_ISDIR(st[ST_MODE])
|
except os.error:
|
||||||
|
return 0
|
||||||
|
return S_ISDIR(st[ST_MODE])
|
||||||
|
|
||||||
|
|
||||||
# Get size, mtime, atime of files.
|
# Get size, mtime, atime of files.
|
||||||
|
@ -117,105 +117,103 @@ def getatime(filename):
|
||||||
return st[ST_MTIME]
|
return st[ST_MTIME]
|
||||||
|
|
||||||
|
|
||||||
# Return true if the pathname refers to a symbolic link.
|
|
||||||
# (Always false on the Mac, until we understand Aliases.)
|
|
||||||
|
|
||||||
def islink(s):
|
def islink(s):
|
||||||
return 0
|
"""Return true if the pathname refers to a symbolic link.
|
||||||
|
Always false on the Mac, until we understand Aliases.)"""
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
# Return true if the pathname refers to an existing regular file.
|
|
||||||
|
|
||||||
def isfile(s):
|
def isfile(s):
|
||||||
try:
|
"""Return true if the pathname refers to an existing regular file."""
|
||||||
st = os.stat(s)
|
|
||||||
except os.error:
|
|
||||||
return 0
|
|
||||||
return S_ISREG(st[ST_MODE])
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
st = os.stat(s)
|
||||||
|
except os.error:
|
||||||
|
return 0
|
||||||
|
return S_ISREG(st[ST_MODE])
|
||||||
|
|
||||||
# Return true if the pathname refers to an existing file or directory.
|
|
||||||
|
|
||||||
def exists(s):
|
def exists(s):
|
||||||
try:
|
"""Return true if the pathname refers to an existing file or directory."""
|
||||||
st = os.stat(s)
|
|
||||||
except os.error:
|
try:
|
||||||
return 0
|
st = os.stat(s)
|
||||||
return 1
|
except os.error:
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# dummy expandvars to retain interface-compatability with other
|
|
||||||
# operating systems.
|
|
||||||
def expandvars(path):
|
def expandvars(path):
|
||||||
return path
|
"""Dummy to retain interface-compatibility with other operating systems."""
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# dummy expanduser to retain interface-compatability with other
|
|
||||||
# operating systems.
|
|
||||||
def expanduser(path):
|
def expanduser(path):
|
||||||
return path
|
"""Dummy to retain interface-compatibility with other operating systems."""
|
||||||
|
return path
|
||||||
# Normalize a pathname: get rid of '::' sequences by backing up,
|
|
||||||
# e.g., 'foo:bar::bletch' becomes 'foo:bletch'.
|
|
||||||
# Raise the exception norm_error below if backing up is impossible,
|
|
||||||
# e.g., for '::foo'.
|
|
||||||
# XXX The Unix version doesn't raise an exception but simply
|
|
||||||
# returns an unnormalized path. Should do so here too.
|
|
||||||
|
|
||||||
norm_error = 'macpath.norm_error: path cannot be normalized'
|
norm_error = 'macpath.norm_error: path cannot be normalized'
|
||||||
|
|
||||||
def normpath(s):
|
def normpath(s):
|
||||||
import string
|
"""Normalize a pathname: get rid of '::' sequences by backing up,
|
||||||
if ':' not in s:
|
e.g., 'foo:bar::bletch' becomes 'foo:bletch'.
|
||||||
return ':' + s
|
Raise the exception norm_error below if backing up is impossible,
|
||||||
f = string.splitfields(s, ':')
|
e.g., for '::foo'."""
|
||||||
pre = []
|
# XXX The Unix version doesn't raise an exception but simply
|
||||||
post = []
|
# returns an unnormalized path. Should do so here too.
|
||||||
if not f[0]:
|
|
||||||
pre = f[:1]
|
|
||||||
f = f[1:]
|
|
||||||
if not f[len(f)-1]:
|
|
||||||
post = f[-1:]
|
|
||||||
f = f[:-1]
|
|
||||||
res = []
|
|
||||||
for seg in f:
|
|
||||||
if seg:
|
|
||||||
res.append(seg)
|
|
||||||
else:
|
|
||||||
if not res: raise norm_error, 'path starts with ::'
|
|
||||||
del res[len(res)-1]
|
|
||||||
if not (pre or res):
|
|
||||||
raise norm_error, 'path starts with volume::'
|
|
||||||
if pre: res = pre + res
|
|
||||||
if post: res = res + post
|
|
||||||
s = res[0]
|
|
||||||
for seg in res[1:]:
|
|
||||||
s = s + ':' + seg
|
|
||||||
return s
|
|
||||||
|
|
||||||
|
import string
|
||||||
|
if ':' not in s:
|
||||||
|
return ':' + s
|
||||||
|
f = string.splitfields(s, ':')
|
||||||
|
pre = []
|
||||||
|
post = []
|
||||||
|
if not f[0]:
|
||||||
|
pre = f[:1]
|
||||||
|
f = f[1:]
|
||||||
|
if not f[len(f)-1]:
|
||||||
|
post = f[-1:]
|
||||||
|
f = f[:-1]
|
||||||
|
res = []
|
||||||
|
for seg in f:
|
||||||
|
if seg:
|
||||||
|
res.append(seg)
|
||||||
|
else:
|
||||||
|
if not res: raise norm_error, 'path starts with ::'
|
||||||
|
del res[len(res)-1]
|
||||||
|
if not (pre or res):
|
||||||
|
raise norm_error, 'path starts with volume::'
|
||||||
|
if pre: res = pre + res
|
||||||
|
if post: res = res + post
|
||||||
|
s = res[0]
|
||||||
|
for seg in res[1:]:
|
||||||
|
s = s + ':' + seg
|
||||||
|
return s
|
||||||
|
|
||||||
# Directory tree walk.
|
|
||||||
# For each directory under top (including top itself),
|
|
||||||
# func(arg, dirname, filenames) is called, where
|
|
||||||
# dirname is the name of the directory and filenames is the list
|
|
||||||
# of files (and subdirectories etc.) in the directory.
|
|
||||||
# The func may modify the filenames list, to implement a filter,
|
|
||||||
# or to impose a different order of visiting.
|
|
||||||
|
|
||||||
def walk(top, func, arg):
|
def walk(top, func, arg):
|
||||||
try:
|
"""Directory tree walk.
|
||||||
names = os.listdir(top)
|
For each directory under top (including top itself),
|
||||||
except os.error:
|
func(arg, dirname, filenames) is called, where
|
||||||
return
|
dirname is the name of the directory and filenames is the list
|
||||||
func(arg, top, names)
|
of files (and subdirectories etc.) in the directory.
|
||||||
for name in names:
|
The func may modify the filenames list, to implement a filter,
|
||||||
name = join(top, name)
|
or to impose a different order of visiting."""
|
||||||
if isdir(name):
|
|
||||||
walk(name, func, arg)
|
try:
|
||||||
|
names = os.listdir(top)
|
||||||
|
except os.error:
|
||||||
|
return
|
||||||
|
func(arg, top, names)
|
||||||
|
for name in names:
|
||||||
|
name = join(top, name)
|
||||||
|
if isdir(name):
|
||||||
|
walk(name, func, arg)
|
||||||
|
|
||||||
|
|
||||||
# Return an absolute path.
|
|
||||||
def abspath(path):
|
def abspath(path):
|
||||||
|
"""Return an absolute path."""
|
||||||
if not isabs(path):
|
if not isabs(path):
|
||||||
path = join(os.getcwd(), path)
|
path = join(os.getcwd(), path)
|
||||||
return normpath(path)
|
return normpath(path)
|
||||||
|
|
|
@ -9,8 +9,11 @@ import string
|
||||||
def getcaps():
|
def getcaps():
|
||||||
"""Return a dictionary containing the mailcap database.
|
"""Return a dictionary containing the mailcap database.
|
||||||
|
|
||||||
The dictionary maps a MIME type (in all lowercase,
|
The dictionary maps a MIME type (in all lowercase, e.g. 'text/plain')
|
||||||
e.g. 'text/plain') to a list of corresponding mailcap entries.
|
to a list of dictionaries corresponding to mailcap entries. The list
|
||||||
|
collects all the entries for that MIME type from all available mailcap
|
||||||
|
files. Each dictionary contains key-value pairs for that MIME type,
|
||||||
|
where the viewing command is stored with the key "view".
|
||||||
|
|
||||||
"""
|
"""
|
||||||
caps = {}
|
caps = {}
|
||||||
|
@ -48,6 +51,14 @@ def listmailcapfiles():
|
||||||
# Part 2: the parser.
|
# Part 2: the parser.
|
||||||
|
|
||||||
def readmailcapfile(fp):
|
def readmailcapfile(fp):
|
||||||
|
"""Read a mailcap file and return a dictionary keyed by MIME type.
|
||||||
|
|
||||||
|
Each MIME type is mapped to an entry consisting of a list of
|
||||||
|
dictionaries; the list will contain more than one such dictionary
|
||||||
|
if a given MIME type appears more than once in the mailcap file.
|
||||||
|
Each dictionary contains key-value pairs for that MIME type, where
|
||||||
|
the viewing command is stored with the key "view".
|
||||||
|
"""
|
||||||
caps = {}
|
caps = {}
|
||||||
while 1:
|
while 1:
|
||||||
line = fp.readline()
|
line = fp.readline()
|
||||||
|
@ -78,6 +89,11 @@ def readmailcapfile(fp):
|
||||||
return caps
|
return caps
|
||||||
|
|
||||||
def parseline(line):
|
def parseline(line):
|
||||||
|
"""Parse one entry in a mailcap file and return a dictionary.
|
||||||
|
|
||||||
|
The viewing command is stored as the value with the key "view",
|
||||||
|
and the rest of the fields produce key-value pairs in the dict.
|
||||||
|
"""
|
||||||
fields = []
|
fields = []
|
||||||
i, n = 0, len(line)
|
i, n = 0, len(line)
|
||||||
while i < n:
|
while i < n:
|
||||||
|
@ -104,6 +120,7 @@ def parseline(line):
|
||||||
return key, fields
|
return key, fields
|
||||||
|
|
||||||
def parsefield(line, i, n):
|
def parsefield(line, i, n):
|
||||||
|
"""Separate one key-value pair in a mailcap entry."""
|
||||||
start = i
|
start = i
|
||||||
while i < n:
|
while i < n:
|
||||||
c = line[i]
|
c = line[i]
|
||||||
|
|
297
Lib/mhlib.py
297
Lib/mhlib.py
|
@ -1,57 +1,58 @@
|
||||||
# MH interface -- purely object-oriented (well, almost)
|
"""MH interface -- purely object-oriented (well, almost)
|
||||||
#
|
|
||||||
# Executive summary:
|
Executive summary:
|
||||||
#
|
|
||||||
# import mhlib
|
import mhlib
|
||||||
#
|
|
||||||
# mh = mhlib.MH() # use default mailbox directory and profile
|
mh = mhlib.MH() # use default mailbox directory and profile
|
||||||
# mh = mhlib.MH(mailbox) # override mailbox location (default from profile)
|
mh = mhlib.MH(mailbox) # override mailbox location (default from profile)
|
||||||
# mh = mhlib.MH(mailbox, profile) # override mailbox and profile
|
mh = mhlib.MH(mailbox, profile) # override mailbox and profile
|
||||||
#
|
|
||||||
# mh.error(format, ...) # print error message -- can be overridden
|
mh.error(format, ...) # print error message -- can be overridden
|
||||||
# s = mh.getprofile(key) # profile entry (None if not set)
|
s = mh.getprofile(key) # profile entry (None if not set)
|
||||||
# path = mh.getpath() # mailbox pathname
|
path = mh.getpath() # mailbox pathname
|
||||||
# name = mh.getcontext() # name of current folder
|
name = mh.getcontext() # name of current folder
|
||||||
# mh.setcontext(name) # set name of current folder
|
mh.setcontext(name) # set name of current folder
|
||||||
#
|
|
||||||
# list = mh.listfolders() # names of top-level folders
|
list = mh.listfolders() # names of top-level folders
|
||||||
# list = mh.listallfolders() # names of all folders, including subfolders
|
list = mh.listallfolders() # names of all folders, including subfolders
|
||||||
# list = mh.listsubfolders(name) # direct subfolders of given folder
|
list = mh.listsubfolders(name) # direct subfolders of given folder
|
||||||
# list = mh.listallsubfolders(name) # all subfolders of given folder
|
list = mh.listallsubfolders(name) # all subfolders of given folder
|
||||||
#
|
|
||||||
# mh.makefolder(name) # create new folder
|
mh.makefolder(name) # create new folder
|
||||||
# mh.deletefolder(name) # delete folder -- must have no subfolders
|
mh.deletefolder(name) # delete folder -- must have no subfolders
|
||||||
#
|
|
||||||
# f = mh.openfolder(name) # new open folder object
|
f = mh.openfolder(name) # new open folder object
|
||||||
#
|
|
||||||
# f.error(format, ...) # same as mh.error(format, ...)
|
f.error(format, ...) # same as mh.error(format, ...)
|
||||||
# path = f.getfullname() # folder's full pathname
|
path = f.getfullname() # folder's full pathname
|
||||||
# path = f.getsequencesfilename() # full pathname of folder's sequences file
|
path = f.getsequencesfilename() # full pathname of folder's sequences file
|
||||||
# path = f.getmessagefilename(n) # full pathname of message n in folder
|
path = f.getmessagefilename(n) # full pathname of message n in folder
|
||||||
#
|
|
||||||
# list = f.listmessages() # list of messages in folder (as numbers)
|
list = f.listmessages() # list of messages in folder (as numbers)
|
||||||
# n = f.getcurrent() # get current message
|
n = f.getcurrent() # get current message
|
||||||
# f.setcurrent(n) # set current message
|
f.setcurrent(n) # set current message
|
||||||
# list = f.parsesequence(seq) # parse msgs syntax into list of messages
|
list = f.parsesequence(seq) # parse msgs syntax into list of messages
|
||||||
# n = f.getlast() # get last message (0 if no messagse)
|
n = f.getlast() # get last message (0 if no messagse)
|
||||||
# f.setlast(n) # set last message (internal use only)
|
f.setlast(n) # set last message (internal use only)
|
||||||
#
|
|
||||||
# dict = f.getsequences() # dictionary of sequences in folder {name: list}
|
dict = f.getsequences() # dictionary of sequences in folder {name: list}
|
||||||
# f.putsequences(dict) # write sequences back to folder
|
f.putsequences(dict) # write sequences back to folder
|
||||||
#
|
|
||||||
# f.createmessage(n, fp) # add message from file f as number n
|
f.createmessage(n, fp) # add message from file f as number n
|
||||||
# f.removemessages(list) # remove messages in list from folder
|
f.removemessages(list) # remove messages in list from folder
|
||||||
# f.refilemessages(list, tofolder) # move messages in list to other folder
|
f.refilemessages(list, tofolder) # move messages in list to other folder
|
||||||
# f.movemessage(n, tofolder, ton) # move one message to a given destination
|
f.movemessage(n, tofolder, ton) # move one message to a given destination
|
||||||
# f.copymessage(n, tofolder, ton) # copy one message to a given destination
|
f.copymessage(n, tofolder, ton) # copy one message to a given destination
|
||||||
#
|
|
||||||
# m = f.openmessage(n) # new open message object (costs a file descriptor)
|
m = f.openmessage(n) # new open message object (costs a file descriptor)
|
||||||
# m is a derived class of mimetools.Message(rfc822.Message), with:
|
m is a derived class of mimetools.Message(rfc822.Message), with:
|
||||||
# s = m.getheadertext() # text of message's headers
|
s = m.getheadertext() # text of message's headers
|
||||||
# s = m.getheadertext(pred) # text of message's headers, filtered by pred
|
s = m.getheadertext(pred) # text of message's headers, filtered by pred
|
||||||
# s = m.getbodytext() # text of message's body, decoded
|
s = m.getbodytext() # text of message's body, decoded
|
||||||
# s = m.getbodytext(0) # text of message's body, not decoded
|
s = m.getbodytext(0) # text of message's body, not decoded
|
||||||
#
|
"""
|
||||||
|
|
||||||
# XXX To do, functionality:
|
# XXX To do, functionality:
|
||||||
# - annotate messages
|
# - annotate messages
|
||||||
# - send messages
|
# - send messages
|
||||||
|
@ -87,16 +88,15 @@ from bisect import bisect
|
||||||
Error = 'mhlib.Error'
|
Error = 'mhlib.Error'
|
||||||
|
|
||||||
|
|
||||||
# Class representing a particular collection of folders.
|
|
||||||
# Optional constructor arguments are the pathname for the directory
|
|
||||||
# containing the collection, and the MH profile to use.
|
|
||||||
# If either is omitted or empty a default is used; the default
|
|
||||||
# directory is taken from the MH profile if it is specified there.
|
|
||||||
|
|
||||||
class MH:
|
class MH:
|
||||||
|
"""Class representing a particular collection of folders.
|
||||||
|
Optional constructor arguments are the pathname for the directory
|
||||||
|
containing the collection, and the MH profile to use.
|
||||||
|
If either is omitted or empty a default is used; the default
|
||||||
|
directory is taken from the MH profile if it is specified there."""
|
||||||
|
|
||||||
# Constructor
|
|
||||||
def __init__(self, path = None, profile = None):
|
def __init__(self, path = None, profile = None):
|
||||||
|
"""Constructor."""
|
||||||
if not profile: profile = MH_PROFILE
|
if not profile: profile = MH_PROFILE
|
||||||
self.profile = os.path.expanduser(profile)
|
self.profile = os.path.expanduser(profile)
|
||||||
if not path: path = self.getprofile('Path')
|
if not path: path = self.getprofile('Path')
|
||||||
|
@ -107,38 +107,38 @@ class MH:
|
||||||
if not os.path.isdir(path): raise Error, 'MH() path not found'
|
if not os.path.isdir(path): raise Error, 'MH() path not found'
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
# String representation
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
"""String representation."""
|
||||||
return 'MH(%s, %s)' % (`self.path`, `self.profile`)
|
return 'MH(%s, %s)' % (`self.path`, `self.profile`)
|
||||||
|
|
||||||
# Routine to print an error. May be overridden by a derived class
|
|
||||||
def error(self, msg, *args):
|
def error(self, msg, *args):
|
||||||
|
"""Routine to print an error. May be overridden by a derived class."""
|
||||||
sys.stderr.write('MH error: %s\n' % (msg % args))
|
sys.stderr.write('MH error: %s\n' % (msg % args))
|
||||||
|
|
||||||
# Return a profile entry, None if not found
|
|
||||||
def getprofile(self, key):
|
def getprofile(self, key):
|
||||||
|
"""Return a profile entry, None if not found."""
|
||||||
return pickline(self.profile, key)
|
return pickline(self.profile, key)
|
||||||
|
|
||||||
# Return the path (the name of the collection's directory)
|
|
||||||
def getpath(self):
|
def getpath(self):
|
||||||
|
"""Return the path (the name of the collection's directory)."""
|
||||||
return self.path
|
return self.path
|
||||||
|
|
||||||
# Return the name of the current folder
|
|
||||||
def getcontext(self):
|
def getcontext(self):
|
||||||
|
"""Return the name of the current folder."""
|
||||||
context = pickline(os.path.join(self.getpath(), 'context'),
|
context = pickline(os.path.join(self.getpath(), 'context'),
|
||||||
'Current-Folder')
|
'Current-Folder')
|
||||||
if not context: context = 'inbox'
|
if not context: context = 'inbox'
|
||||||
return context
|
return context
|
||||||
|
|
||||||
# Set the name of the current folder
|
|
||||||
def setcontext(self, context):
|
def setcontext(self, context):
|
||||||
|
"""Set the name of the current folder."""
|
||||||
fn = os.path.join(self.getpath(), 'context')
|
fn = os.path.join(self.getpath(), 'context')
|
||||||
f = open(fn, "w")
|
f = open(fn, "w")
|
||||||
f.write("Current-Folder: %s\n" % context)
|
f.write("Current-Folder: %s\n" % context)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
# Return the names of the top-level folders
|
|
||||||
def listfolders(self):
|
def listfolders(self):
|
||||||
|
"""Return the names of the top-level folders."""
|
||||||
folders = []
|
folders = []
|
||||||
path = self.getpath()
|
path = self.getpath()
|
||||||
for name in os.listdir(path):
|
for name in os.listdir(path):
|
||||||
|
@ -148,9 +148,9 @@ class MH:
|
||||||
folders.sort()
|
folders.sort()
|
||||||
return folders
|
return folders
|
||||||
|
|
||||||
# Return the names of the subfolders in a given folder
|
|
||||||
# (prefixed with the given folder name)
|
|
||||||
def listsubfolders(self, name):
|
def listsubfolders(self, name):
|
||||||
|
"""Return the names of the subfolders in a given folder
|
||||||
|
(prefixed with the given folder name)."""
|
||||||
fullname = os.path.join(self.path, name)
|
fullname = os.path.join(self.path, name)
|
||||||
# Get the link count so we can avoid listing folders
|
# Get the link count so we can avoid listing folders
|
||||||
# that have no subfolders.
|
# that have no subfolders.
|
||||||
|
@ -173,12 +173,12 @@ class MH:
|
||||||
subfolders.sort()
|
subfolders.sort()
|
||||||
return subfolders
|
return subfolders
|
||||||
|
|
||||||
# Return the names of all folders, including subfolders, recursively
|
|
||||||
def listallfolders(self):
|
def listallfolders(self):
|
||||||
|
"""Return the names of all folders and subfolders, recursively."""
|
||||||
return self.listallsubfolders('')
|
return self.listallsubfolders('')
|
||||||
|
|
||||||
# Return the names of subfolders in a given folder, recursively
|
|
||||||
def listallsubfolders(self, name):
|
def listallsubfolders(self, name):
|
||||||
|
"""Return the names of subfolders in a given folder, recursively."""
|
||||||
fullname = os.path.join(self.path, name)
|
fullname = os.path.join(self.path, name)
|
||||||
# Get the link count so we can avoid listing folders
|
# Get the link count so we can avoid listing folders
|
||||||
# that have no subfolders.
|
# that have no subfolders.
|
||||||
|
@ -206,13 +206,12 @@ class MH:
|
||||||
subfolders.sort()
|
subfolders.sort()
|
||||||
return subfolders
|
return subfolders
|
||||||
|
|
||||||
# Return a new Folder object for the named folder
|
|
||||||
def openfolder(self, name):
|
def openfolder(self, name):
|
||||||
|
"""Return a new Folder object for the named folder."""
|
||||||
return Folder(self, name)
|
return Folder(self, name)
|
||||||
|
|
||||||
# Create a new folder. This raises os.error if the folder
|
|
||||||
# cannot be created
|
|
||||||
def makefolder(self, name):
|
def makefolder(self, name):
|
||||||
|
"""Create a new folder (or raise os.error if it cannot be created)."""
|
||||||
protect = pickline(self.profile, 'Folder-Protect')
|
protect = pickline(self.profile, 'Folder-Protect')
|
||||||
if protect and isnumeric(protect):
|
if protect and isnumeric(protect):
|
||||||
mode = string.atoi(protect, 8)
|
mode = string.atoi(protect, 8)
|
||||||
|
@ -220,10 +219,9 @@ class MH:
|
||||||
mode = FOLDER_PROTECT
|
mode = FOLDER_PROTECT
|
||||||
os.mkdir(os.path.join(self.getpath(), name), mode)
|
os.mkdir(os.path.join(self.getpath(), name), mode)
|
||||||
|
|
||||||
# Delete a folder. This removes files in the folder but not
|
|
||||||
# subdirectories. If deleting the folder itself fails it
|
|
||||||
# raises os.error
|
|
||||||
def deletefolder(self, name):
|
def deletefolder(self, name):
|
||||||
|
"""Delete a folder. This removes files in the folder but not
|
||||||
|
subdirectories. Raise os.error if deleting the folder itself fails."""
|
||||||
fullname = os.path.join(self.getpath(), name)
|
fullname = os.path.join(self.getpath(), name)
|
||||||
for subname in os.listdir(fullname):
|
for subname in os.listdir(fullname):
|
||||||
fullsubname = os.path.join(fullname, subname)
|
fullsubname = os.path.join(fullname, subname)
|
||||||
|
@ -235,52 +233,51 @@ class MH:
|
||||||
os.rmdir(fullname)
|
os.rmdir(fullname)
|
||||||
|
|
||||||
|
|
||||||
# Class representing a particular folder
|
|
||||||
|
|
||||||
numericprog = re.compile('^[1-9][0-9]*$')
|
numericprog = re.compile('^[1-9][0-9]*$')
|
||||||
def isnumeric(str):
|
def isnumeric(str):
|
||||||
return numericprog.match(str) is not None
|
return numericprog.match(str) is not None
|
||||||
|
|
||||||
class Folder:
|
class Folder:
|
||||||
|
"""Class representing a particular folder."""
|
||||||
|
|
||||||
# Constructor
|
|
||||||
def __init__(self, mh, name):
|
def __init__(self, mh, name):
|
||||||
|
"""Constructor."""
|
||||||
self.mh = mh
|
self.mh = mh
|
||||||
self.name = name
|
self.name = name
|
||||||
if not os.path.isdir(self.getfullname()):
|
if not os.path.isdir(self.getfullname()):
|
||||||
raise Error, 'no folder %s' % name
|
raise Error, 'no folder %s' % name
|
||||||
|
|
||||||
# String representation
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
"""String representation."""
|
||||||
return 'Folder(%s, %s)' % (`self.mh`, `self.name`)
|
return 'Folder(%s, %s)' % (`self.mh`, `self.name`)
|
||||||
|
|
||||||
# Error message handler
|
|
||||||
def error(self, *args):
|
def error(self, *args):
|
||||||
|
"""Error message handler."""
|
||||||
apply(self.mh.error, args)
|
apply(self.mh.error, args)
|
||||||
|
|
||||||
# Return the full pathname of the folder
|
|
||||||
def getfullname(self):
|
def getfullname(self):
|
||||||
|
"""Return the full pathname of the folder."""
|
||||||
return os.path.join(self.mh.path, self.name)
|
return os.path.join(self.mh.path, self.name)
|
||||||
|
|
||||||
# Return the full pathname of the folder's sequences file
|
|
||||||
def getsequencesfilename(self):
|
def getsequencesfilename(self):
|
||||||
|
"""Return the full pathname of the folder's sequences file."""
|
||||||
return os.path.join(self.getfullname(), MH_SEQUENCES)
|
return os.path.join(self.getfullname(), MH_SEQUENCES)
|
||||||
|
|
||||||
# Return the full pathname of a message in the folder
|
|
||||||
def getmessagefilename(self, n):
|
def getmessagefilename(self, n):
|
||||||
|
"""Return the full pathname of a message in the folder."""
|
||||||
return os.path.join(self.getfullname(), str(n))
|
return os.path.join(self.getfullname(), str(n))
|
||||||
|
|
||||||
# Return list of direct subfolders
|
|
||||||
def listsubfolders(self):
|
def listsubfolders(self):
|
||||||
|
"""Return list of direct subfolders."""
|
||||||
return self.mh.listsubfolders(self.name)
|
return self.mh.listsubfolders(self.name)
|
||||||
|
|
||||||
# Return list of all subfolders
|
|
||||||
def listallsubfolders(self):
|
def listallsubfolders(self):
|
||||||
|
"""Return list of all subfolders."""
|
||||||
return self.mh.listallsubfolders(self.name)
|
return self.mh.listallsubfolders(self.name)
|
||||||
|
|
||||||
# Return the list of messages currently present in the folder.
|
|
||||||
# As a side effect, set self.last to the last message (or 0)
|
|
||||||
def listmessages(self):
|
def listmessages(self):
|
||||||
|
"""Return the list of messages currently present in the folder.
|
||||||
|
As a side effect, set self.last to the last message (or 0)."""
|
||||||
messages = []
|
messages = []
|
||||||
match = numericprog.match
|
match = numericprog.match
|
||||||
append = messages.append
|
append = messages.append
|
||||||
|
@ -295,8 +292,8 @@ class Folder:
|
||||||
self.last = 0
|
self.last = 0
|
||||||
return messages
|
return messages
|
||||||
|
|
||||||
# Return the set of sequences for the folder
|
|
||||||
def getsequences(self):
|
def getsequences(self):
|
||||||
|
"""Return the set of sequences for the folder."""
|
||||||
sequences = {}
|
sequences = {}
|
||||||
fullname = self.getsequencesfilename()
|
fullname = self.getsequencesfilename()
|
||||||
try:
|
try:
|
||||||
|
@ -315,8 +312,8 @@ class Folder:
|
||||||
sequences[key] = value
|
sequences[key] = value
|
||||||
return sequences
|
return sequences
|
||||||
|
|
||||||
# Write the set of sequences back to the folder
|
|
||||||
def putsequences(self, sequences):
|
def putsequences(self, sequences):
|
||||||
|
"""Write the set of sequences back to the folder."""
|
||||||
fullname = self.getsequencesfilename()
|
fullname = self.getsequencesfilename()
|
||||||
f = None
|
f = None
|
||||||
for key in sequences.keys():
|
for key in sequences.keys():
|
||||||
|
@ -332,23 +329,23 @@ class Folder:
|
||||||
else:
|
else:
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
# Return the current message. Raise KeyError when there is none
|
|
||||||
def getcurrent(self):
|
def getcurrent(self):
|
||||||
|
"""Return the current message. Raise KeyError when there is none."""
|
||||||
seqs = self.getsequences()
|
seqs = self.getsequences()
|
||||||
try:
|
try:
|
||||||
return max(seqs['cur'])
|
return max(seqs['cur'])
|
||||||
except (ValueError, KeyError):
|
except (ValueError, KeyError):
|
||||||
raise Error, "no cur message"
|
raise Error, "no cur message"
|
||||||
|
|
||||||
# Set the current message
|
|
||||||
def setcurrent(self, n):
|
def setcurrent(self, n):
|
||||||
|
"""Set the current message."""
|
||||||
updateline(self.getsequencesfilename(), 'cur', str(n), 0)
|
updateline(self.getsequencesfilename(), 'cur', str(n), 0)
|
||||||
|
|
||||||
# Parse an MH sequence specification into a message list.
|
|
||||||
# Attempt to mimic mh-sequence(5) as close as possible.
|
|
||||||
# Also attempt to mimic observed behavior regarding which
|
|
||||||
# conditions cause which error messages
|
|
||||||
def parsesequence(self, seq):
|
def parsesequence(self, seq):
|
||||||
|
"""Parse an MH sequence specification into a message list.
|
||||||
|
Attempt to mimic mh-sequence(5) as close as possible.
|
||||||
|
Also attempt to mimic observed behavior regarding which
|
||||||
|
conditions cause which error messages."""
|
||||||
# XXX Still not complete (see mh-format(5)).
|
# XXX Still not complete (see mh-format(5)).
|
||||||
# Missing are:
|
# Missing are:
|
||||||
# - 'prev', 'next' as count
|
# - 'prev', 'next' as count
|
||||||
|
@ -428,8 +425,8 @@ class Folder:
|
||||||
else:
|
else:
|
||||||
return [n]
|
return [n]
|
||||||
|
|
||||||
# Internal: parse a message number (or cur, first, etc.)
|
|
||||||
def _parseindex(self, seq, all):
|
def _parseindex(self, seq, all):
|
||||||
|
"""Internal: parse a message number (or cur, first, etc.)."""
|
||||||
if isnumeric(seq):
|
if isnumeric(seq):
|
||||||
try:
|
try:
|
||||||
return string.atoi(seq)
|
return string.atoi(seq)
|
||||||
|
@ -459,12 +456,12 @@ class Folder:
|
||||||
raise Error, "no prev message"
|
raise Error, "no prev message"
|
||||||
raise Error, None
|
raise Error, None
|
||||||
|
|
||||||
# Open a message -- returns a Message object
|
|
||||||
def openmessage(self, n):
|
def openmessage(self, n):
|
||||||
|
"""Open a message -- returns a Message object."""
|
||||||
return Message(self, n)
|
return Message(self, n)
|
||||||
|
|
||||||
# Remove one or more messages -- may raise os.error
|
|
||||||
def removemessages(self, list):
|
def removemessages(self, list):
|
||||||
|
"""Remove one or more messages -- may raise os.error."""
|
||||||
errors = []
|
errors = []
|
||||||
deleted = []
|
deleted = []
|
||||||
for n in list:
|
for n in list:
|
||||||
|
@ -488,9 +485,9 @@ class Folder:
|
||||||
else:
|
else:
|
||||||
raise os.error, ('multiple errors:', errors)
|
raise os.error, ('multiple errors:', errors)
|
||||||
|
|
||||||
# Refile one or more messages -- may raise os.error.
|
|
||||||
# 'tofolder' is an open folder object
|
|
||||||
def refilemessages(self, list, tofolder, keepsequences=0):
|
def refilemessages(self, list, tofolder, keepsequences=0):
|
||||||
|
"""Refile one or more messages -- may raise os.error.
|
||||||
|
'tofolder' is an open folder object."""
|
||||||
errors = []
|
errors = []
|
||||||
refiled = {}
|
refiled = {}
|
||||||
for n in list:
|
for n in list:
|
||||||
|
@ -523,8 +520,8 @@ class Folder:
|
||||||
else:
|
else:
|
||||||
raise os.error, ('multiple errors:', errors)
|
raise os.error, ('multiple errors:', errors)
|
||||||
|
|
||||||
# Helper for refilemessages() to copy sequences
|
|
||||||
def _copysequences(self, fromfolder, refileditems):
|
def _copysequences(self, fromfolder, refileditems):
|
||||||
|
"""Helper for refilemessages() to copy sequences."""
|
||||||
fromsequences = fromfolder.getsequences()
|
fromsequences = fromfolder.getsequences()
|
||||||
tosequences = self.getsequences()
|
tosequences = self.getsequences()
|
||||||
changed = 0
|
changed = 0
|
||||||
|
@ -544,9 +541,9 @@ class Folder:
|
||||||
if changed:
|
if changed:
|
||||||
self.putsequences(tosequences)
|
self.putsequences(tosequences)
|
||||||
|
|
||||||
# Move one message over a specific destination message,
|
|
||||||
# which may or may not already exist.
|
|
||||||
def movemessage(self, n, tofolder, ton):
|
def movemessage(self, n, tofolder, ton):
|
||||||
|
"""Move one message over a specific destination message,
|
||||||
|
which may or may not already exist."""
|
||||||
path = self.getmessagefilename(n)
|
path = self.getmessagefilename(n)
|
||||||
# Open it to check that it exists
|
# Open it to check that it exists
|
||||||
f = open(path)
|
f = open(path)
|
||||||
|
@ -576,9 +573,9 @@ class Folder:
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
self.removefromallsequences([n])
|
self.removefromallsequences([n])
|
||||||
|
|
||||||
# Copy one message over a specific destination message,
|
|
||||||
# which may or may not already exist.
|
|
||||||
def copymessage(self, n, tofolder, ton):
|
def copymessage(self, n, tofolder, ton):
|
||||||
|
"""Copy one message over a specific destination message,
|
||||||
|
which may or may not already exist."""
|
||||||
path = self.getmessagefilename(n)
|
path = self.getmessagefilename(n)
|
||||||
# Open it to check that it exists
|
# Open it to check that it exists
|
||||||
f = open(path)
|
f = open(path)
|
||||||
|
@ -602,8 +599,8 @@ class Folder:
|
||||||
except os.error:
|
except os.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Create a message, with text from the open file txt.
|
|
||||||
def createmessage(self, n, txt):
|
def createmessage(self, n, txt):
|
||||||
|
"""Create a message, with text from the open file txt."""
|
||||||
path = self.getmessagefilename(n)
|
path = self.getmessagefilename(n)
|
||||||
backuppath = self.getmessagefilename(',%d' % n)
|
backuppath = self.getmessagefilename(',%d' % n)
|
||||||
try:
|
try:
|
||||||
|
@ -628,9 +625,9 @@ class Folder:
|
||||||
except os.error:
|
except os.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Remove one or more messages from all sequeuces (including last)
|
|
||||||
# -- but not from 'cur'!!!
|
|
||||||
def removefromallsequences(self, list):
|
def removefromallsequences(self, list):
|
||||||
|
"""Remove one or more messages from all sequeuces (including last)
|
||||||
|
-- but not from 'cur'!!!"""
|
||||||
if hasattr(self, 'last') and self.last in list:
|
if hasattr(self, 'last') and self.last in list:
|
||||||
del self.last
|
del self.last
|
||||||
sequences = self.getsequences()
|
sequences = self.getsequences()
|
||||||
|
@ -647,14 +644,14 @@ class Folder:
|
||||||
if changed:
|
if changed:
|
||||||
self.putsequences(sequences)
|
self.putsequences(sequences)
|
||||||
|
|
||||||
# Return the last message number
|
|
||||||
def getlast(self):
|
def getlast(self):
|
||||||
|
"""Return the last message number."""
|
||||||
if not hasattr(self, 'last'):
|
if not hasattr(self, 'last'):
|
||||||
messages = self.listmessages()
|
messages = self.listmessages()
|
||||||
return self.last
|
return self.last
|
||||||
|
|
||||||
# Set the last message number
|
|
||||||
def setlast(self, last):
|
def setlast(self, last):
|
||||||
|
"""Set the last message number."""
|
||||||
if last is None:
|
if last is None:
|
||||||
if hasattr(self, 'last'):
|
if hasattr(self, 'last'):
|
||||||
del self.last
|
del self.last
|
||||||
|
@ -663,8 +660,8 @@ class Folder:
|
||||||
|
|
||||||
class Message(mimetools.Message):
|
class Message(mimetools.Message):
|
||||||
|
|
||||||
# Constructor
|
|
||||||
def __init__(self, f, n, fp = None):
|
def __init__(self, f, n, fp = None):
|
||||||
|
"""Constructor."""
|
||||||
self.folder = f
|
self.folder = f
|
||||||
self.number = n
|
self.number = n
|
||||||
if not fp:
|
if not fp:
|
||||||
|
@ -672,15 +669,15 @@ class Message(mimetools.Message):
|
||||||
fp = open(path, 'r')
|
fp = open(path, 'r')
|
||||||
mimetools.Message.__init__(self, fp)
|
mimetools.Message.__init__(self, fp)
|
||||||
|
|
||||||
# String representation
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
"""String representation."""
|
||||||
return 'Message(%s, %s)' % (repr(self.folder), self.number)
|
return 'Message(%s, %s)' % (repr(self.folder), self.number)
|
||||||
|
|
||||||
# Return the message's header text as a string. If an
|
|
||||||
# argument is specified, it is used as a filter predicate to
|
|
||||||
# decide which headers to return (its argument is the header
|
|
||||||
# name converted to lower case).
|
|
||||||
def getheadertext(self, pred = None):
|
def getheadertext(self, pred = None):
|
||||||
|
"""Return the message's header text as a string. If an
|
||||||
|
argument is specified, it is used as a filter predicate to
|
||||||
|
decide which headers to return (its argument is the header
|
||||||
|
name converted to lower case)."""
|
||||||
if not pred:
|
if not pred:
|
||||||
return string.joinfields(self.headers, '')
|
return string.joinfields(self.headers, '')
|
||||||
headers = []
|
headers = []
|
||||||
|
@ -693,11 +690,11 @@ class Message(mimetools.Message):
|
||||||
if hit: headers.append(line)
|
if hit: headers.append(line)
|
||||||
return string.joinfields(headers, '')
|
return string.joinfields(headers, '')
|
||||||
|
|
||||||
# Return the message's body text as string. This undoes a
|
|
||||||
# Content-Transfer-Encoding, but does not interpret other MIME
|
|
||||||
# features (e.g. multipart messages). To suppress to
|
|
||||||
# decoding, pass a 0 as argument
|
|
||||||
def getbodytext(self, decode = 1):
|
def getbodytext(self, decode = 1):
|
||||||
|
"""Return the message's body text as string. This undoes a
|
||||||
|
Content-Transfer-Encoding, but does not interpret other MIME
|
||||||
|
features (e.g. multipart messages). To suppress decoding,
|
||||||
|
pass 0 as an argument."""
|
||||||
self.fp.seek(self.startofbody)
|
self.fp.seek(self.startofbody)
|
||||||
encoding = self.getencoding()
|
encoding = self.getencoding()
|
||||||
if not decode or encoding in ('', '7bit', '8bit', 'binary'):
|
if not decode or encoding in ('', '7bit', '8bit', 'binary'):
|
||||||
|
@ -707,10 +704,10 @@ class Message(mimetools.Message):
|
||||||
mimetools.decode(self.fp, output, encoding)
|
mimetools.decode(self.fp, output, encoding)
|
||||||
return output.getvalue()
|
return output.getvalue()
|
||||||
|
|
||||||
# Only for multipart messages: return the message's body as a
|
|
||||||
# list of SubMessage objects. Each submessage object behaves
|
|
||||||
# (almost) as a Message object.
|
|
||||||
def getbodyparts(self):
|
def getbodyparts(self):
|
||||||
|
"""Only for multipart messages: return the message's body as a
|
||||||
|
list of SubMessage objects. Each submessage object behaves
|
||||||
|
(almost) as a Message object."""
|
||||||
if self.getmaintype() != 'multipart':
|
if self.getmaintype() != 'multipart':
|
||||||
raise Error, 'Content-Type is not multipart/*'
|
raise Error, 'Content-Type is not multipart/*'
|
||||||
bdry = self.getparam('boundary')
|
bdry = self.getparam('boundary')
|
||||||
|
@ -727,8 +724,8 @@ class Message(mimetools.Message):
|
||||||
mf.pop()
|
mf.pop()
|
||||||
return parts
|
return parts
|
||||||
|
|
||||||
# Return body, either a string or a list of messages
|
|
||||||
def getbody(self):
|
def getbody(self):
|
||||||
|
"""Return body, either a string or a list of messages."""
|
||||||
if self.getmaintype() == 'multipart':
|
if self.getmaintype() == 'multipart':
|
||||||
return self.getbodyparts()
|
return self.getbodyparts()
|
||||||
else:
|
else:
|
||||||
|
@ -737,8 +734,8 @@ class Message(mimetools.Message):
|
||||||
|
|
||||||
class SubMessage(Message):
|
class SubMessage(Message):
|
||||||
|
|
||||||
# Constructor
|
|
||||||
def __init__(self, f, n, fp):
|
def __init__(self, f, n, fp):
|
||||||
|
"""Constructor."""
|
||||||
Message.__init__(self, f, n, fp)
|
Message.__init__(self, f, n, fp)
|
||||||
if self.getmaintype() == 'multipart':
|
if self.getmaintype() == 'multipart':
|
||||||
self.body = Message.getbodyparts(self)
|
self.body = Message.getbodyparts(self)
|
||||||
|
@ -747,8 +744,8 @@ class SubMessage(Message):
|
||||||
self.bodyencoded = Message.getbodytext(self, decode=0)
|
self.bodyencoded = Message.getbodytext(self, decode=0)
|
||||||
# XXX If this is big, should remember file pointers
|
# XXX If this is big, should remember file pointers
|
||||||
|
|
||||||
# String representation
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
"""String representation."""
|
||||||
f, n, fp = self.folder, self.number, self.fp
|
f, n, fp = self.folder, self.number, self.fp
|
||||||
return 'SubMessage(%s, %s, %s)' % (f, n, fp)
|
return 'SubMessage(%s, %s, %s)' % (f, n, fp)
|
||||||
|
|
||||||
|
@ -766,28 +763,28 @@ class SubMessage(Message):
|
||||||
return self.body
|
return self.body
|
||||||
|
|
||||||
|
|
||||||
# Class implementing sets of integers.
|
|
||||||
#
|
|
||||||
# This is an efficient representation for sets consisting of several
|
|
||||||
# continuous ranges, e.g. 1-100,200-400,402-1000 is represented
|
|
||||||
# internally as a list of three pairs: [(1,100), (200,400),
|
|
||||||
# (402,1000)]. The internal representation is always kept normalized.
|
|
||||||
#
|
|
||||||
# The constructor has up to three arguments:
|
|
||||||
# - the string used to initialize the set (default ''),
|
|
||||||
# - the separator between ranges (default ',')
|
|
||||||
# - the separator between begin and end of a range (default '-')
|
|
||||||
# The separators must be strings (not regexprs) and should be different.
|
|
||||||
#
|
|
||||||
# The tostring() function yields a string that can be passed to another
|
|
||||||
# IntSet constructor; __repr__() is a valid IntSet constructor itself.
|
|
||||||
#
|
|
||||||
# XXX The default begin/end separator means that negative numbers are
|
|
||||||
# not supported very well.
|
|
||||||
#
|
|
||||||
# XXX There are currently no operations to remove set elements.
|
|
||||||
|
|
||||||
class IntSet:
|
class IntSet:
|
||||||
|
"""Class implementing sets of integers.
|
||||||
|
|
||||||
|
This is an efficient representation for sets consisting of several
|
||||||
|
continuous ranges, e.g. 1-100,200-400,402-1000 is represented
|
||||||
|
internally as a list of three pairs: [(1,100), (200,400),
|
||||||
|
(402,1000)]. The internal representation is always kept normalized.
|
||||||
|
|
||||||
|
The constructor has up to three arguments:
|
||||||
|
- the string used to initialize the set (default ''),
|
||||||
|
- the separator between ranges (default ',')
|
||||||
|
- the separator between begin and end of a range (default '-')
|
||||||
|
The separators must be strings (not regexprs) and should be different.
|
||||||
|
|
||||||
|
The tostring() function yields a string that can be passed to another
|
||||||
|
IntSet constructor; __repr__() is a valid IntSet constructor itself.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# XXX The default begin/end separator means that negative numbers are
|
||||||
|
# not supported very well.
|
||||||
|
#
|
||||||
|
# XXX There are currently no operations to remove set elements.
|
||||||
|
|
||||||
def __init__(self, data = None, sep = ',', rng = '-'):
|
def __init__(self, data = None, sep = ',', rng = '-'):
|
||||||
self.pairs = []
|
self.pairs = []
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Various tools used by MIME-reading or MIME-writing programs.
|
"""Various tools used by MIME-reading or MIME-writing programs."""
|
||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -7,10 +7,9 @@ import string
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
|
||||||
# A derived class of rfc822.Message that knows about MIME headers and
|
|
||||||
# contains some hooks for decoding encoded and multipart messages.
|
|
||||||
|
|
||||||
class Message(rfc822.Message):
|
class Message(rfc822.Message):
|
||||||
|
"""A derived class of rfc822.Message that knows about MIME headers and
|
||||||
|
contains some hooks for decoding encoded and multipart messages."""
|
||||||
|
|
||||||
def __init__(self, fp, seekable = 1):
|
def __init__(self, fp, seekable = 1):
|
||||||
rfc822.Message.__init__(self, fp, seekable)
|
rfc822.Message.__init__(self, fp, seekable)
|
||||||
|
@ -96,17 +95,17 @@ class Message(rfc822.Message):
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|
||||||
|
|
||||||
# Return a random string usable as a multipart boundary.
|
|
||||||
# The method used is so that it is *very* unlikely that the same
|
|
||||||
# string of characters will every occur again in the Universe,
|
|
||||||
# so the caller needn't check the data it is packing for the
|
|
||||||
# occurrence of the boundary.
|
|
||||||
#
|
|
||||||
# The boundary contains dots so you have to quote it in the header.
|
|
||||||
|
|
||||||
_prefix = None
|
_prefix = None
|
||||||
|
|
||||||
def choose_boundary():
|
def choose_boundary():
|
||||||
|
"""Return a random string usable as a multipart boundary.
|
||||||
|
The method used is so that it is *very* unlikely that the same
|
||||||
|
string of characters will every occur again in the Universe,
|
||||||
|
so the caller needn't check the data it is packing for the
|
||||||
|
occurrence of the boundary.
|
||||||
|
|
||||||
|
The boundary contains dots so you have to quote it in the header."""
|
||||||
|
|
||||||
global _prefix
|
global _prefix
|
||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
|
@ -131,6 +130,7 @@ def choose_boundary():
|
||||||
# Subroutines for decoding some common content-transfer-types
|
# Subroutines for decoding some common content-transfer-types
|
||||||
|
|
||||||
def decode(input, output, encoding):
|
def decode(input, output, encoding):
|
||||||
|
"""Decode common content-transfer-encodings (base64, quopri, uuencode)."""
|
||||||
if encoding == 'base64':
|
if encoding == 'base64':
|
||||||
import base64
|
import base64
|
||||||
return base64.decode(input, output)
|
return base64.decode(input, output)
|
||||||
|
@ -147,6 +147,7 @@ def decode(input, output, encoding):
|
||||||
'unknown Content-Transfer-Encoding: %s' % encoding
|
'unknown Content-Transfer-Encoding: %s' % encoding
|
||||||
|
|
||||||
def encode(input, output, encoding):
|
def encode(input, output, encoding):
|
||||||
|
"""Encode common content-transfer-encodings (base64, quopri, uuencode)."""
|
||||||
if encoding == 'base64':
|
if encoding == 'base64':
|
||||||
import base64
|
import base64
|
||||||
return base64.encode(input, output)
|
return base64.encode(input, output)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
'''Mimification and unmimification of mail messages.
|
'''Mimification and unmimification of mail messages.
|
||||||
|
|
||||||
decode quoted-printable parts of a mail message or encode using
|
Decode quoted-printable parts of a mail message or encode using
|
||||||
quoted-printable.
|
quoted-printable.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
@ -39,9 +39,8 @@ mime_head = re.compile('=\\?iso-8859-1\\?q\\?([^? \t\n]+)\\?=', re.I)
|
||||||
repl = re.compile('^subject:\\s+re: ', re.I)
|
repl = re.compile('^subject:\\s+re: ', re.I)
|
||||||
|
|
||||||
class File:
|
class File:
|
||||||
'''A simple fake file object that knows about limited
|
"""A simple fake file object that knows about limited read-ahead and
|
||||||
read-ahead and boundaries.
|
boundaries. The only supported method is readline()."""
|
||||||
The only supported method is readline().'''
|
|
||||||
|
|
||||||
def __init__(self, file, boundary):
|
def __init__(self, file, boundary):
|
||||||
self.file = file
|
self.file = file
|
||||||
|
@ -87,7 +86,7 @@ class HeaderFile:
|
||||||
self.peek = None
|
self.peek = None
|
||||||
|
|
||||||
def mime_decode(line):
|
def mime_decode(line):
|
||||||
'''Decode a single line of quoted-printable text to 8bit.'''
|
"""Decode a single line of quoted-printable text to 8bit."""
|
||||||
newline = ''
|
newline = ''
|
||||||
pos = 0
|
pos = 0
|
||||||
while 1:
|
while 1:
|
||||||
|
@ -100,7 +99,7 @@ def mime_decode(line):
|
||||||
return newline + line[pos:]
|
return newline + line[pos:]
|
||||||
|
|
||||||
def mime_decode_header(line):
|
def mime_decode_header(line):
|
||||||
'''Decode a header line to 8bit.'''
|
"""Decode a header line to 8bit."""
|
||||||
newline = ''
|
newline = ''
|
||||||
pos = 0
|
pos = 0
|
||||||
while 1:
|
while 1:
|
||||||
|
@ -115,7 +114,7 @@ def mime_decode_header(line):
|
||||||
return newline + line[pos:]
|
return newline + line[pos:]
|
||||||
|
|
||||||
def unmimify_part(ifile, ofile, decode_base64 = 0):
|
def unmimify_part(ifile, ofile, decode_base64 = 0):
|
||||||
'''Convert a quoted-printable part of a MIME mail message to 8bit.'''
|
"""Convert a quoted-printable part of a MIME mail message to 8bit."""
|
||||||
multipart = None
|
multipart = None
|
||||||
quoted_printable = 0
|
quoted_printable = 0
|
||||||
is_base64 = 0
|
is_base64 = 0
|
||||||
|
@ -200,7 +199,7 @@ def unmimify_part(ifile, ofile, decode_base64 = 0):
|
||||||
ofile.write(pref + line)
|
ofile.write(pref + line)
|
||||||
|
|
||||||
def unmimify(infile, outfile, decode_base64 = 0):
|
def unmimify(infile, outfile, decode_base64 = 0):
|
||||||
'''Convert quoted-printable parts of a MIME mail message to 8bit.'''
|
"""Convert quoted-printable parts of a MIME mail message to 8bit."""
|
||||||
if type(infile) == type(''):
|
if type(infile) == type(''):
|
||||||
ifile = open(infile)
|
ifile = open(infile)
|
||||||
if type(outfile) == type('') and infile == outfile:
|
if type(outfile) == type('') and infile == outfile:
|
||||||
|
@ -221,8 +220,8 @@ mime_char = re.compile('[=\177-\377]') # quote these chars in body
|
||||||
mime_header_char = re.compile('[=?\177-\377]') # quote these in header
|
mime_header_char = re.compile('[=?\177-\377]') # quote these in header
|
||||||
|
|
||||||
def mime_encode(line, header):
|
def mime_encode(line, header):
|
||||||
'''Code a single line as quoted-printable.
|
"""Code a single line as quoted-printable.
|
||||||
If header is set, quote some extra characters.'''
|
If header is set, quote some extra characters."""
|
||||||
if header:
|
if header:
|
||||||
reg = mime_header_char
|
reg = mime_header_char
|
||||||
else:
|
else:
|
||||||
|
@ -255,7 +254,7 @@ def mime_encode(line, header):
|
||||||
mime_header = re.compile('([ \t(]|^)([-a-zA-Z0-9_+]*[\177-\377][-a-zA-Z0-9_+\177-\377]*)([ \t)]|\n)')
|
mime_header = re.compile('([ \t(]|^)([-a-zA-Z0-9_+]*[\177-\377][-a-zA-Z0-9_+\177-\377]*)([ \t)]|\n)')
|
||||||
|
|
||||||
def mime_encode_header(line):
|
def mime_encode_header(line):
|
||||||
'''Code a single header line as quoted-printable.'''
|
"""Code a single header line as quoted-printable."""
|
||||||
newline = ''
|
newline = ''
|
||||||
pos = 0
|
pos = 0
|
||||||
while 1:
|
while 1:
|
||||||
|
@ -273,7 +272,7 @@ cte = re.compile('^content-transfer-encoding:', re.I)
|
||||||
iso_char = re.compile('[\177-\377]')
|
iso_char = re.compile('[\177-\377]')
|
||||||
|
|
||||||
def mimify_part(ifile, ofile, is_mime):
|
def mimify_part(ifile, ofile, is_mime):
|
||||||
'''Convert an 8bit part of a MIME mail message to quoted-printable.'''
|
"""Convert an 8bit part of a MIME mail message to quoted-printable."""
|
||||||
has_cte = is_qp = is_base64 = 0
|
has_cte = is_qp = is_base64 = 0
|
||||||
multipart = None
|
multipart = None
|
||||||
must_quote_body = must_quote_header = has_iso_chars = 0
|
must_quote_body = must_quote_header = has_iso_chars = 0
|
||||||
|
@ -408,7 +407,7 @@ def mimify_part(ifile, ofile, is_mime):
|
||||||
ofile.write(line)
|
ofile.write(line)
|
||||||
|
|
||||||
def mimify(infile, outfile):
|
def mimify(infile, outfile):
|
||||||
'''Convert 8bit parts of a MIME mail message to quoted-printable.'''
|
"""Convert 8bit parts of a MIME mail message to quoted-printable."""
|
||||||
if type(infile) == type(''):
|
if type(infile) == type(''):
|
||||||
ifile = open(infile)
|
ifile = open(infile)
|
||||||
if type(outfile) == type('') and infile == outfile:
|
if type(outfile) == type('') and infile == outfile:
|
||||||
|
|
|
@ -1,28 +1,31 @@
|
||||||
# A class that makes each part of a multipart message "feel" like an
|
"""A readline()-style interface to the parts of a multipart message.
|
||||||
# ordinary file, as long as you use fp.readline(). Allows recursive
|
|
||||||
# use, for nested multipart messages. Probably best used together
|
The MultiFile class makes each part of a multipart message "feel" like
|
||||||
# with module mimetools.
|
an ordinary file, as long as you use fp.readline(). Allows recursive
|
||||||
#
|
use, for nested multipart messages. Probably best used together
|
||||||
# Suggested use:
|
with module mimetools.
|
||||||
#
|
|
||||||
# real_fp = open(...)
|
Suggested use:
|
||||||
# fp = MultiFile(real_fp)
|
|
||||||
#
|
real_fp = open(...)
|
||||||
# "read some lines from fp"
|
fp = MultiFile(real_fp)
|
||||||
# fp.push(separator)
|
|
||||||
# while 1:
|
"read some lines from fp"
|
||||||
# "read lines from fp until it returns an empty string" (A)
|
fp.push(separator)
|
||||||
# if not fp.next(): break
|
while 1:
|
||||||
# fp.pop()
|
"read lines from fp until it returns an empty string" (A)
|
||||||
# "read remaining lines from fp until it returns an empty string"
|
if not fp.next(): break
|
||||||
#
|
fp.pop()
|
||||||
# The latter sequence may be used recursively at (A).
|
"read remaining lines from fp until it returns an empty string"
|
||||||
# It is also allowed to use multiple push()...pop() sequences.
|
|
||||||
#
|
The latter sequence may be used recursively at (A).
|
||||||
# If seekable is given as 0, the class code will not do the bookeeping
|
It is also allowed to use multiple push()...pop() sequences.
|
||||||
# it normally attempts in order to make seeks relative to the beginning of the
|
|
||||||
# current file part. This may be useful when using MultiFile with a non-
|
If seekable is given as 0, the class code will not do the bookeeping
|
||||||
# seekable stream object.
|
it normally attempts in order to make seeks relative to the beginning of the
|
||||||
|
current file part. This may be useful when using MultiFile with a non-
|
||||||
|
seekable stream object.
|
||||||
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import string
|
import string
|
||||||
|
@ -30,9 +33,9 @@ import string
|
||||||
Error = 'multifile.Error'
|
Error = 'multifile.Error'
|
||||||
|
|
||||||
class MultiFile:
|
class MultiFile:
|
||||||
#
|
|
||||||
seekable = 0
|
seekable = 0
|
||||||
#
|
|
||||||
def __init__(self, fp, seekable=1):
|
def __init__(self, fp, seekable=1):
|
||||||
self.fp = fp
|
self.fp = fp
|
||||||
self.stack = [] # Grows down
|
self.stack = [] # Grows down
|
||||||
|
@ -42,12 +45,12 @@ class MultiFile:
|
||||||
self.seekable = 1
|
self.seekable = 1
|
||||||
self.start = self.fp.tell()
|
self.start = self.fp.tell()
|
||||||
self.posstack = [] # Grows down
|
self.posstack = [] # Grows down
|
||||||
#
|
|
||||||
def tell(self):
|
def tell(self):
|
||||||
if self.level > 0:
|
if self.level > 0:
|
||||||
return self.lastpos
|
return self.lastpos
|
||||||
return self.fp.tell() - self.start
|
return self.fp.tell() - self.start
|
||||||
#
|
|
||||||
def seek(self, pos, whence=0):
|
def seek(self, pos, whence=0):
|
||||||
here = self.tell()
|
here = self.tell()
|
||||||
if whence:
|
if whence:
|
||||||
|
@ -64,7 +67,7 @@ class MultiFile:
|
||||||
self.fp.seek(pos + self.start)
|
self.fp.seek(pos + self.start)
|
||||||
self.level = 0
|
self.level = 0
|
||||||
self.last = 0
|
self.last = 0
|
||||||
#
|
|
||||||
def readline(self):
|
def readline(self):
|
||||||
if self.level > 0:
|
if self.level > 0:
|
||||||
return ''
|
return ''
|
||||||
|
@ -105,7 +108,7 @@ class MultiFile:
|
||||||
if self.level > 1:
|
if self.level > 1:
|
||||||
raise Error,'Missing endmarker in MultiFile.readline()'
|
raise Error,'Missing endmarker in MultiFile.readline()'
|
||||||
return ''
|
return ''
|
||||||
#
|
|
||||||
def readlines(self):
|
def readlines(self):
|
||||||
list = []
|
list = []
|
||||||
while 1:
|
while 1:
|
||||||
|
@ -113,10 +116,10 @@ class MultiFile:
|
||||||
if not line: break
|
if not line: break
|
||||||
list.append(line)
|
list.append(line)
|
||||||
return list
|
return list
|
||||||
#
|
|
||||||
def read(self): # Note: no size argument -- read until EOF only!
|
def read(self): # Note: no size argument -- read until EOF only!
|
||||||
return string.joinfields(self.readlines(), '')
|
return string.joinfields(self.readlines(), '')
|
||||||
#
|
|
||||||
def next(self):
|
def next(self):
|
||||||
while self.readline(): pass
|
while self.readline(): pass
|
||||||
if self.level > 1 or self.last:
|
if self.level > 1 or self.last:
|
||||||
|
@ -126,7 +129,7 @@ class MultiFile:
|
||||||
if self.seekable:
|
if self.seekable:
|
||||||
self.start = self.fp.tell()
|
self.start = self.fp.tell()
|
||||||
return 1
|
return 1
|
||||||
#
|
|
||||||
def push(self, sep):
|
def push(self, sep):
|
||||||
if self.level > 0:
|
if self.level > 0:
|
||||||
raise Error, 'bad MultiFile.push() call'
|
raise Error, 'bad MultiFile.push() call'
|
||||||
|
@ -134,7 +137,7 @@ class MultiFile:
|
||||||
if self.seekable:
|
if self.seekable:
|
||||||
self.posstack.insert(0, self.start)
|
self.posstack.insert(0, self.start)
|
||||||
self.start = self.fp.tell()
|
self.start = self.fp.tell()
|
||||||
#
|
|
||||||
def pop(self):
|
def pop(self):
|
||||||
if self.stack == []:
|
if self.stack == []:
|
||||||
raise Error, 'bad MultiFile.pop() call'
|
raise Error, 'bad MultiFile.pop() call'
|
||||||
|
@ -149,12 +152,12 @@ class MultiFile:
|
||||||
del self.posstack[0]
|
del self.posstack[0]
|
||||||
if self.level > 0:
|
if self.level > 0:
|
||||||
self.lastpos = abslastpos - self.start
|
self.lastpos = abslastpos - self.start
|
||||||
#
|
|
||||||
def is_data(self, line):
|
def is_data(self, line):
|
||||||
return line[:2] <> '--'
|
return line[:2] <> '--'
|
||||||
#
|
|
||||||
def section_divider(self, str):
|
def section_divider(self, str):
|
||||||
return "--" + str
|
return "--" + str
|
||||||
#
|
|
||||||
def end_marker(self, str):
|
def end_marker(self, str):
|
||||||
return "--" + str + "--"
|
return "--" + str + "--"
|
||||||
|
|
59
Lib/mutex.py
59
Lib/mutex.py
|
@ -1,58 +1,51 @@
|
||||||
# Mutual exclusion -- for use with module sched
|
"""Mutual exclusion -- for use with module sched
|
||||||
|
|
||||||
|
A mutex has two pieces of state -- a 'locked' bit and a queue.
|
||||||
|
When the mutex is not locked, the queue is empty.
|
||||||
|
Otherwise, the queue contains 0 or more (function, argument) pairs
|
||||||
|
representing functions (or methods) waiting to acquire the lock.
|
||||||
|
When the mutex is unlocked while the queue is not empty,
|
||||||
|
the first queue entry is removed and its function(argument) pair called,
|
||||||
|
implying it now has the lock.
|
||||||
|
|
||||||
|
Of course, no multi-threading is implied -- hence the funny interface
|
||||||
|
for lock, where a function is called once the lock is aquired.
|
||||||
|
"""
|
||||||
|
|
||||||
# A mutex has two pieces of state -- a 'locked' bit and a queue.
|
|
||||||
# When the mutex is not locked, the queue is empty.
|
|
||||||
# Otherwise, the queue contains 0 or more (function, argument) pairs
|
|
||||||
# representing functions (or methods) waiting to acquire the lock.
|
|
||||||
# When the mutex is unlocked while the queue is not empty,
|
|
||||||
# the first queue entry is removed and its function(argument) pair called,
|
|
||||||
# implying it now has the lock.
|
|
||||||
#
|
|
||||||
# Of course, no multi-threading is implied -- hence the funny interface
|
|
||||||
# for lock, where a function is called once the lock is aquired.
|
|
||||||
#
|
|
||||||
class mutex:
|
class mutex:
|
||||||
#
|
|
||||||
# Create a new mutex -- initially unlocked
|
|
||||||
#
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""Create a new mutex -- initially unlocked."""
|
||||||
self.locked = 0
|
self.locked = 0
|
||||||
self.queue = []
|
self.queue = []
|
||||||
#
|
|
||||||
# Test the locked bit of the mutex
|
|
||||||
#
|
|
||||||
def test(self):
|
def test(self):
|
||||||
|
"""Test the locked bit of the mutex."""
|
||||||
return self.locked
|
return self.locked
|
||||||
#
|
|
||||||
# Atomic test-and-set -- grab the lock if it is not set,
|
|
||||||
# return true if it succeeded
|
|
||||||
#
|
|
||||||
def testandset(self):
|
def testandset(self):
|
||||||
|
"""Atomic test-and-set -- grab the lock if it is not set,
|
||||||
|
return true if it succeeded."""
|
||||||
if not self.locked:
|
if not self.locked:
|
||||||
self.locked = 1
|
self.locked = 1
|
||||||
return 1
|
return 1
|
||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
#
|
|
||||||
# Lock a mutex, call the function with supplied argument
|
|
||||||
# when it is acquired.
|
|
||||||
# If the mutex is already locked, place function and argument
|
|
||||||
# in the queue.
|
|
||||||
#
|
|
||||||
def lock(self, function, argument):
|
def lock(self, function, argument):
|
||||||
|
"""Lock a mutex, call the function with supplied argument
|
||||||
|
when it is acquired. If the mutex is already locked, place
|
||||||
|
function and argument in the queue."""
|
||||||
if self.testandset():
|
if self.testandset():
|
||||||
function(argument)
|
function(argument)
|
||||||
else:
|
else:
|
||||||
self.queue.append((function, argument))
|
self.queue.append((function, argument))
|
||||||
#
|
|
||||||
# Unlock a mutex. If the queue is not empty, call the next
|
|
||||||
# function with its argument.
|
|
||||||
#
|
|
||||||
def unlock(self):
|
def unlock(self):
|
||||||
|
"""Unlock a mutex. If the queue is not empty, call the next
|
||||||
|
function with its argument."""
|
||||||
if self.queue:
|
if self.queue:
|
||||||
function, argument = self.queue[0]
|
function, argument = self.queue[0]
|
||||||
del self.queue[0]
|
del self.queue[0]
|
||||||
function(argument)
|
function(argument)
|
||||||
else:
|
else:
|
||||||
self.locked = 0
|
self.locked = 0
|
||||||
#
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
"""An object-oriented interface to .netrc files."""
|
||||||
|
|
||||||
# Module and documentation by Eric S. Raymond, 21 Dec 1998
|
# Module and documentation by Eric S. Raymond, 21 Dec 1998
|
||||||
|
|
||||||
import os, shlex
|
import os, shlex
|
||||||
|
@ -63,7 +65,7 @@ class netrc:
|
||||||
raise SyntaxError, "bad follower token %s, file %s, line %d"%(tt,file,lexer.lineno)
|
raise SyntaxError, "bad follower token %s, file %s, line %d"%(tt,file,lexer.lineno)
|
||||||
|
|
||||||
def authenticators(self, host):
|
def authenticators(self, host):
|
||||||
"Return a (user, account, password) tuple for given host."
|
"""Return a (user, account, password) tuple for given host."""
|
||||||
if self.hosts.has_key(host):
|
if self.hosts.has_key(host):
|
||||||
return self.hosts[host]
|
return self.hosts[host]
|
||||||
elif self.hosts.has_key('default'):
|
elif self.hosts.has_key('default'):
|
||||||
|
@ -72,7 +74,7 @@ class netrc:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"Dump the class data in the format of a .netrc file"
|
"""Dump the class data in the format of a .netrc file."""
|
||||||
rep = ""
|
rep = ""
|
||||||
for host in self.hosts.keys():
|
for host in self.hosts.keys():
|
||||||
attrs = self.hosts[host]
|
attrs = self.hosts[host]
|
||||||
|
|
342
Lib/nntplib.py
342
Lib/nntplib.py
|
@ -1,31 +1,31 @@
|
||||||
# An NNTP client class. Based on RFC 977: Network News Transfer
|
"""An NNTP client class based on RFC 977: Network News Transfer Protocol.
|
||||||
# Protocol, by Brian Kantor and Phil Lapsley.
|
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
# Example:
|
>>> from nntplib import NNTP
|
||||||
#
|
>>> s = NNTP('news')
|
||||||
# >>> from nntplib import NNTP
|
>>> resp, count, first, last, name = s.group('comp.lang.python')
|
||||||
# >>> s = NNTP('news')
|
>>> print 'Group', name, 'has', count, 'articles, range', first, 'to', last
|
||||||
# >>> resp, count, first, last, name = s.group('comp.lang.python')
|
Group comp.lang.python has 51 articles, range 5770 to 5821
|
||||||
# >>> print 'Group', name, 'has', count, 'articles, range', first, 'to', last
|
>>> resp, subs = s.xhdr('subject', first + '-' + last)
|
||||||
# Group comp.lang.python has 51 articles, range 5770 to 5821
|
>>> resp = s.quit()
|
||||||
# >>> resp, subs = s.xhdr('subject', first + '-' + last)
|
>>>
|
||||||
# >>> resp = s.quit()
|
|
||||||
# >>>
|
|
||||||
#
|
|
||||||
# Here 'resp' is the server response line.
|
|
||||||
# Error responses are turned into exceptions.
|
|
||||||
#
|
|
||||||
# To post an article from a file:
|
|
||||||
# >>> f = open(filename, 'r') # file containing article, including header
|
|
||||||
# >>> resp = s.post(f)
|
|
||||||
# >>>
|
|
||||||
#
|
|
||||||
# For descriptions of all methods, read the comments in the code below.
|
|
||||||
# Note that all arguments and return values representing article numbers
|
|
||||||
# are strings, not numbers, since they are rarely used for calculations.
|
|
||||||
|
|
||||||
# (xover, xgtitle, xpath, date methods by Kevan Heydon)
|
Here 'resp' is the server response line.
|
||||||
|
Error responses are turned into exceptions.
|
||||||
|
|
||||||
|
To post an article from a file:
|
||||||
|
>>> f = open(filename, 'r') # file containing article, including header
|
||||||
|
>>> resp = s.post(f)
|
||||||
|
>>>
|
||||||
|
|
||||||
|
For descriptions of all methods, read the comments in the code below.
|
||||||
|
Note that all arguments and return values representing article numbers
|
||||||
|
are strings, not numbers, since they are rarely used for calculations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# RFC 977 by Brian Kantor and Phil Lapsley.
|
||||||
|
# xover, xgtitle, xpath, date methods by Kevan Heydon
|
||||||
|
|
||||||
|
|
||||||
# Imports
|
# Imports
|
||||||
|
@ -35,7 +35,6 @@ import string
|
||||||
|
|
||||||
|
|
||||||
# Exception raised when an error or invalid response is received
|
# Exception raised when an error or invalid response is received
|
||||||
|
|
||||||
error_reply = 'nntplib.error_reply' # unexpected [123]xx reply
|
error_reply = 'nntplib.error_reply' # unexpected [123]xx reply
|
||||||
error_temp = 'nntplib.error_temp' # 4xx errors
|
error_temp = 'nntplib.error_temp' # 4xx errors
|
||||||
error_perm = 'nntplib.error_perm' # 5xx errors
|
error_perm = 'nntplib.error_perm' # 5xx errors
|
||||||
|
@ -59,11 +58,11 @@ CRLF = '\r\n'
|
||||||
|
|
||||||
class NNTP:
|
class NNTP:
|
||||||
|
|
||||||
# Initialize an instance. Arguments:
|
|
||||||
# - host: hostname to connect to
|
|
||||||
# - port: port to connect to (default the standard NNTP port)
|
|
||||||
|
|
||||||
def __init__(self, host, port = NNTP_PORT, user=None, password=None):
|
def __init__(self, host, port = NNTP_PORT, user=None, password=None):
|
||||||
|
"""Initialize an instance. Arguments:
|
||||||
|
- host: hostname to connect to
|
||||||
|
- port: port to connect to (default the standard NNTP port)"""
|
||||||
|
|
||||||
self.host = host
|
self.host = host
|
||||||
self.port = port
|
self.port = port
|
||||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
@ -82,38 +81,38 @@ class NNTP:
|
||||||
if resp[:3] != '281':
|
if resp[:3] != '281':
|
||||||
raise error_perm, resp
|
raise error_perm, resp
|
||||||
|
|
||||||
# Get the welcome message from the server
|
|
||||||
# (this is read and squirreled away by __init__()).
|
|
||||||
# If the response code is 200, posting is allowed;
|
|
||||||
# if it 201, posting is not allowed
|
|
||||||
|
|
||||||
def getwelcome(self):
|
def getwelcome(self):
|
||||||
|
"""Get the welcome message from the server
|
||||||
|
(this is read and squirreled away by __init__()).
|
||||||
|
If the response code is 200, posting is allowed;
|
||||||
|
if it 201, posting is not allowed."""
|
||||||
|
|
||||||
if self.debugging: print '*welcome*', `self.welcome`
|
if self.debugging: print '*welcome*', `self.welcome`
|
||||||
return self.welcome
|
return self.welcome
|
||||||
|
|
||||||
# Set the debugging level. Argument level means:
|
|
||||||
# 0: no debugging output (default)
|
|
||||||
# 1: print commands and responses but not body text etc.
|
|
||||||
# 2: also print raw lines read and sent before stripping CR/LF
|
|
||||||
|
|
||||||
def set_debuglevel(self, level):
|
def set_debuglevel(self, level):
|
||||||
|
"""Set the debugging level. Argument 'level' means:
|
||||||
|
0: no debugging output (default)
|
||||||
|
1: print commands and responses but not body text etc.
|
||||||
|
2: also print raw lines read and sent before stripping CR/LF"""
|
||||||
|
|
||||||
self.debugging = level
|
self.debugging = level
|
||||||
debug = set_debuglevel
|
debug = set_debuglevel
|
||||||
|
|
||||||
# Internal: send one line to the server, appending CRLF
|
|
||||||
def putline(self, line):
|
def putline(self, line):
|
||||||
|
"""Internal: send one line to the server, appending CRLF."""
|
||||||
line = line + CRLF
|
line = line + CRLF
|
||||||
if self.debugging > 1: print '*put*', `line`
|
if self.debugging > 1: print '*put*', `line`
|
||||||
self.sock.send(line)
|
self.sock.send(line)
|
||||||
|
|
||||||
# Internal: send one command to the server (through putline())
|
|
||||||
def putcmd(self, line):
|
def putcmd(self, line):
|
||||||
|
"""Internal: send one command to the server (through putline())."""
|
||||||
if self.debugging: print '*cmd*', `line`
|
if self.debugging: print '*cmd*', `line`
|
||||||
self.putline(line)
|
self.putline(line)
|
||||||
|
|
||||||
# Internal: return one line from the server, stripping CRLF.
|
|
||||||
# Raise EOFError if the connection is closed
|
|
||||||
def getline(self):
|
def getline(self):
|
||||||
|
"""Internal: return one line from the server, stripping CRLF.
|
||||||
|
Raise EOFError if the connection is closed."""
|
||||||
line = self.file.readline()
|
line = self.file.readline()
|
||||||
if self.debugging > 1:
|
if self.debugging > 1:
|
||||||
print '*get*', `line`
|
print '*get*', `line`
|
||||||
|
@ -122,9 +121,9 @@ class NNTP:
|
||||||
elif line[-1:] in CRLF: line = line[:-1]
|
elif line[-1:] in CRLF: line = line[:-1]
|
||||||
return line
|
return line
|
||||||
|
|
||||||
# Internal: get a response from the server.
|
|
||||||
# Raise various errors if the response indicates an error
|
|
||||||
def getresp(self):
|
def getresp(self):
|
||||||
|
"""Internal: get a response from the server.
|
||||||
|
Raise various errors if the response indicates an error."""
|
||||||
resp = self.getline()
|
resp = self.getline()
|
||||||
if self.debugging: print '*resp*', `resp`
|
if self.debugging: print '*resp*', `resp`
|
||||||
c = resp[:1]
|
c = resp[:1]
|
||||||
|
@ -136,9 +135,9 @@ class NNTP:
|
||||||
raise error_proto, resp
|
raise error_proto, resp
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
# Internal: get a response plus following text from the server.
|
|
||||||
# Raise various errors if the response indicates an error
|
|
||||||
def getlongresp(self):
|
def getlongresp(self):
|
||||||
|
"""Internal: get a response plus following text from the server.
|
||||||
|
Raise various errors if the response indicates an error."""
|
||||||
resp = self.getresp()
|
resp = self.getresp()
|
||||||
if resp[:3] not in LONGRESP:
|
if resp[:3] not in LONGRESP:
|
||||||
raise error_reply, resp
|
raise error_reply, resp
|
||||||
|
@ -152,59 +151,59 @@ class NNTP:
|
||||||
list.append(line)
|
list.append(line)
|
||||||
return resp, list
|
return resp, list
|
||||||
|
|
||||||
# Internal: send a command and get the response
|
|
||||||
def shortcmd(self, line):
|
def shortcmd(self, line):
|
||||||
|
"""Internal: send a command and get the response."""
|
||||||
self.putcmd(line)
|
self.putcmd(line)
|
||||||
return self.getresp()
|
return self.getresp()
|
||||||
|
|
||||||
# Internal: send a command and get the response plus following text
|
|
||||||
def longcmd(self, line):
|
def longcmd(self, line):
|
||||||
|
"""Internal: send a command and get the response plus following text."""
|
||||||
self.putcmd(line)
|
self.putcmd(line)
|
||||||
return self.getlongresp()
|
return self.getlongresp()
|
||||||
|
|
||||||
# Process a NEWGROUPS command. Arguments:
|
|
||||||
# - date: string 'yymmdd' indicating the date
|
|
||||||
# - time: string 'hhmmss' indicating the time
|
|
||||||
# Return:
|
|
||||||
# - resp: server response if succesful
|
|
||||||
# - list: list of newsgroup names
|
|
||||||
|
|
||||||
def newgroups(self, date, time):
|
def newgroups(self, date, time):
|
||||||
|
"""Process a NEWGROUPS command. Arguments:
|
||||||
|
- date: string 'yymmdd' indicating the date
|
||||||
|
- time: string 'hhmmss' indicating the time
|
||||||
|
Return:
|
||||||
|
- resp: server response if succesful
|
||||||
|
- list: list of newsgroup names"""
|
||||||
|
|
||||||
return self.longcmd('NEWGROUPS ' + date + ' ' + time)
|
return self.longcmd('NEWGROUPS ' + date + ' ' + time)
|
||||||
|
|
||||||
# Process a NEWNEWS command. Arguments:
|
|
||||||
# - group: group name or '*'
|
|
||||||
# - date: string 'yymmdd' indicating the date
|
|
||||||
# - time: string 'hhmmss' indicating the time
|
|
||||||
# Return:
|
|
||||||
# - resp: server response if succesful
|
|
||||||
# - list: list of article ids
|
|
||||||
|
|
||||||
def newnews(self, group, date, time):
|
def newnews(self, group, date, time):
|
||||||
|
"""Process a NEWNEWS command. Arguments:
|
||||||
|
- group: group name or '*'
|
||||||
|
- date: string 'yymmdd' indicating the date
|
||||||
|
- time: string 'hhmmss' indicating the time
|
||||||
|
Return:
|
||||||
|
- resp: server response if succesful
|
||||||
|
- list: list of article ids"""
|
||||||
|
|
||||||
cmd = 'NEWNEWS ' + group + ' ' + date + ' ' + time
|
cmd = 'NEWNEWS ' + group + ' ' + date + ' ' + time
|
||||||
return self.longcmd(cmd)
|
return self.longcmd(cmd)
|
||||||
|
|
||||||
# Process a LIST command. Return:
|
|
||||||
# - resp: server response if succesful
|
|
||||||
# - list: list of (group, last, first, flag) (strings)
|
|
||||||
|
|
||||||
def list(self):
|
def list(self):
|
||||||
|
"""Process a LIST command. Return:
|
||||||
|
- resp: server response if succesful
|
||||||
|
- list: list of (group, last, first, flag) (strings)"""
|
||||||
|
|
||||||
resp, list = self.longcmd('LIST')
|
resp, list = self.longcmd('LIST')
|
||||||
for i in range(len(list)):
|
for i in range(len(list)):
|
||||||
# Parse lines into "group last first flag"
|
# Parse lines into "group last first flag"
|
||||||
list[i] = tuple(string.split(list[i]))
|
list[i] = tuple(string.split(list[i]))
|
||||||
return resp, list
|
return resp, list
|
||||||
|
|
||||||
# Process a GROUP command. Argument:
|
|
||||||
# - group: the group name
|
|
||||||
# Returns:
|
|
||||||
# - resp: server response if succesful
|
|
||||||
# - count: number of articles (string)
|
|
||||||
# - first: first article number (string)
|
|
||||||
# - last: last article number (string)
|
|
||||||
# - name: the group name
|
|
||||||
|
|
||||||
def group(self, name):
|
def group(self, name):
|
||||||
|
"""Process a GROUP command. Argument:
|
||||||
|
- group: the group name
|
||||||
|
Returns:
|
||||||
|
- resp: server response if succesful
|
||||||
|
- count: number of articles (string)
|
||||||
|
- first: first article number (string)
|
||||||
|
- last: last article number (string)
|
||||||
|
- name: the group name"""
|
||||||
|
|
||||||
resp = self.shortcmd('GROUP ' + name)
|
resp = self.shortcmd('GROUP ' + name)
|
||||||
if resp[:3] <> '211':
|
if resp[:3] <> '211':
|
||||||
raise error_reply, resp
|
raise error_reply, resp
|
||||||
|
@ -221,15 +220,15 @@ class NNTP:
|
||||||
name = string.lower(words[4])
|
name = string.lower(words[4])
|
||||||
return resp, count, first, last, name
|
return resp, count, first, last, name
|
||||||
|
|
||||||
# Process a HELP command. Returns:
|
|
||||||
# - resp: server response if succesful
|
|
||||||
# - list: list of strings
|
|
||||||
|
|
||||||
def help(self):
|
def help(self):
|
||||||
|
"""Process a HELP command. Returns:
|
||||||
|
- resp: server response if succesful
|
||||||
|
- list: list of strings"""
|
||||||
|
|
||||||
return self.longcmd('HELP')
|
return self.longcmd('HELP')
|
||||||
|
|
||||||
# Internal: parse the response of a STAT, NEXT or LAST command
|
|
||||||
def statparse(self, resp):
|
def statparse(self, resp):
|
||||||
|
"""Internal: parse the response of a STAT, NEXT or LAST command."""
|
||||||
if resp[:2] <> '22':
|
if resp[:2] <> '22':
|
||||||
raise error_reply, resp
|
raise error_reply, resp
|
||||||
words = string.split(resp)
|
words = string.split(resp)
|
||||||
|
@ -242,84 +241,82 @@ class NNTP:
|
||||||
id = words[2]
|
id = words[2]
|
||||||
return resp, nr, id
|
return resp, nr, id
|
||||||
|
|
||||||
# Internal: process a STAT, NEXT or LAST command
|
|
||||||
def statcmd(self, line):
|
def statcmd(self, line):
|
||||||
|
"""Internal: process a STAT, NEXT or LAST command."""
|
||||||
resp = self.shortcmd(line)
|
resp = self.shortcmd(line)
|
||||||
return self.statparse(resp)
|
return self.statparse(resp)
|
||||||
|
|
||||||
# Process a STAT command. Argument:
|
|
||||||
# - id: article number or message id
|
|
||||||
# Returns:
|
|
||||||
# - resp: server response if succesful
|
|
||||||
# - nr: the article number
|
|
||||||
# - id: the article id
|
|
||||||
|
|
||||||
def stat(self, id):
|
def stat(self, id):
|
||||||
|
"""Process a STAT command. Argument:
|
||||||
|
- id: article number or message id
|
||||||
|
Returns:
|
||||||
|
- resp: server response if succesful
|
||||||
|
- nr: the article number
|
||||||
|
- id: the article id"""
|
||||||
|
|
||||||
return self.statcmd('STAT ' + id)
|
return self.statcmd('STAT ' + id)
|
||||||
|
|
||||||
# Process a NEXT command. No arguments. Return as for STAT
|
|
||||||
|
|
||||||
def next(self):
|
def next(self):
|
||||||
|
"""Process a NEXT command. No arguments. Return as for STAT."""
|
||||||
return self.statcmd('NEXT')
|
return self.statcmd('NEXT')
|
||||||
|
|
||||||
# Process a LAST command. No arguments. Return as for STAT
|
|
||||||
|
|
||||||
def last(self):
|
def last(self):
|
||||||
|
"""Process a LAST command. No arguments. Return as for STAT."""
|
||||||
return self.statcmd('LAST')
|
return self.statcmd('LAST')
|
||||||
|
|
||||||
# Internal: process a HEAD, BODY or ARTICLE command
|
|
||||||
def artcmd(self, line):
|
def artcmd(self, line):
|
||||||
|
"""Internal: process a HEAD, BODY or ARTICLE command."""
|
||||||
resp, list = self.longcmd(line)
|
resp, list = self.longcmd(line)
|
||||||
resp, nr, id = self.statparse(resp)
|
resp, nr, id = self.statparse(resp)
|
||||||
return resp, nr, id, list
|
return resp, nr, id, list
|
||||||
|
|
||||||
# Process a HEAD command. Argument:
|
|
||||||
# - id: article number or message id
|
|
||||||
# Returns:
|
|
||||||
# - resp: server response if succesful
|
|
||||||
# - nr: article number
|
|
||||||
# - id: message id
|
|
||||||
# - list: the lines of the article's header
|
|
||||||
|
|
||||||
def head(self, id):
|
def head(self, id):
|
||||||
|
"""Process a HEAD command. Argument:
|
||||||
|
- id: article number or message id
|
||||||
|
Returns:
|
||||||
|
- resp: server response if succesful
|
||||||
|
- nr: article number
|
||||||
|
- id: message id
|
||||||
|
- list: the lines of the article's header"""
|
||||||
|
|
||||||
return self.artcmd('HEAD ' + id)
|
return self.artcmd('HEAD ' + id)
|
||||||
|
|
||||||
# Process a BODY command. Argument:
|
|
||||||
# - id: article number or message id
|
|
||||||
# Returns:
|
|
||||||
# - resp: server response if succesful
|
|
||||||
# - nr: article number
|
|
||||||
# - id: message id
|
|
||||||
# - list: the lines of the article's body
|
|
||||||
|
|
||||||
def body(self, id):
|
def body(self, id):
|
||||||
|
"""Process a BODY command. Argument:
|
||||||
|
- id: article number or message id
|
||||||
|
Returns:
|
||||||
|
- resp: server response if succesful
|
||||||
|
- nr: article number
|
||||||
|
- id: message id
|
||||||
|
- list: the lines of the article's body"""
|
||||||
|
|
||||||
return self.artcmd('BODY ' + id)
|
return self.artcmd('BODY ' + id)
|
||||||
|
|
||||||
# Process an ARTICLE command. Argument:
|
|
||||||
# - id: article number or message id
|
|
||||||
# Returns:
|
|
||||||
# - resp: server response if succesful
|
|
||||||
# - nr: article number
|
|
||||||
# - id: message id
|
|
||||||
# - list: the lines of the article
|
|
||||||
|
|
||||||
def article(self, id):
|
def article(self, id):
|
||||||
|
"""Process an ARTICLE command. Argument:
|
||||||
|
- id: article number or message id
|
||||||
|
Returns:
|
||||||
|
- resp: server response if succesful
|
||||||
|
- nr: article number
|
||||||
|
- id: message id
|
||||||
|
- list: the lines of the article"""
|
||||||
|
|
||||||
return self.artcmd('ARTICLE ' + id)
|
return self.artcmd('ARTICLE ' + id)
|
||||||
|
|
||||||
# Process a SLAVE command. Returns:
|
|
||||||
# - resp: server response if succesful
|
|
||||||
|
|
||||||
def slave(self):
|
def slave(self):
|
||||||
|
"""Process a SLAVE command. Returns:
|
||||||
|
- resp: server response if succesful"""
|
||||||
|
|
||||||
return self.shortcmd('SLAVE')
|
return self.shortcmd('SLAVE')
|
||||||
|
|
||||||
# Process an XHDR command (optional server extension). Arguments:
|
|
||||||
# - hdr: the header type (e.g. 'subject')
|
|
||||||
# - str: an article nr, a message id, or a range nr1-nr2
|
|
||||||
# Returns:
|
|
||||||
# - resp: server response if succesful
|
|
||||||
# - list: list of (nr, value) strings
|
|
||||||
|
|
||||||
def xhdr(self, hdr, str):
|
def xhdr(self, hdr, str):
|
||||||
|
"""Process an XHDR command (optional server extension). Arguments:
|
||||||
|
- hdr: the header type (e.g. 'subject')
|
||||||
|
- str: an article nr, a message id, or a range nr1-nr2
|
||||||
|
Returns:
|
||||||
|
- resp: server response if succesful
|
||||||
|
- list: list of (nr, value) strings"""
|
||||||
|
|
||||||
pat = re.compile('^([0-9]+) ?(.*)\n?')
|
pat = re.compile('^([0-9]+) ?(.*)\n?')
|
||||||
resp, lines = self.longcmd('XHDR ' + hdr + ' ' + str)
|
resp, lines = self.longcmd('XHDR ' + hdr + ' ' + str)
|
||||||
for i in range(len(lines)):
|
for i in range(len(lines)):
|
||||||
|
@ -329,14 +326,15 @@ class NNTP:
|
||||||
lines[i] = m.group(1, 2)
|
lines[i] = m.group(1, 2)
|
||||||
return resp, lines
|
return resp, lines
|
||||||
|
|
||||||
# Process an XOVER command (optional server extension) Arguments:
|
|
||||||
# - start: start of range
|
|
||||||
# - end: end of range
|
|
||||||
# Returns:
|
|
||||||
# - resp: server response if succesful
|
|
||||||
# - list: list of (art-nr, subject, poster, date, id, refrences, size, lines)
|
|
||||||
|
|
||||||
def xover(self,start,end):
|
def xover(self,start,end):
|
||||||
|
"""Process an XOVER command (optional server extension) Arguments:
|
||||||
|
- start: start of range
|
||||||
|
- end: end of range
|
||||||
|
Returns:
|
||||||
|
- resp: server response if succesful
|
||||||
|
- list: list of (art-nr, subject, poster, date,
|
||||||
|
id, references, size, lines)"""
|
||||||
|
|
||||||
resp, lines = self.longcmd('XOVER ' + start + '-' + end)
|
resp, lines = self.longcmd('XOVER ' + start + '-' + end)
|
||||||
xover_lines = []
|
xover_lines = []
|
||||||
for line in lines:
|
for line in lines:
|
||||||
|
@ -354,13 +352,13 @@ class NNTP:
|
||||||
raise error_data,line
|
raise error_data,line
|
||||||
return resp,xover_lines
|
return resp,xover_lines
|
||||||
|
|
||||||
# Process an XGTITLE command (optional server extension) Arguments:
|
|
||||||
# - group: group name wildcard (i.e. news.*)
|
|
||||||
# Returns:
|
|
||||||
# - resp: server response if succesful
|
|
||||||
# - list: list of (name,title) strings
|
|
||||||
|
|
||||||
def xgtitle(self, group):
|
def xgtitle(self, group):
|
||||||
|
"""Process an XGTITLE command (optional server extension) Arguments:
|
||||||
|
- group: group name wildcard (i.e. news.*)
|
||||||
|
Returns:
|
||||||
|
- resp: server response if succesful
|
||||||
|
- list: list of (name,title) strings"""
|
||||||
|
|
||||||
line_pat = re.compile("^([^ \t]+)[ \t]+(.*)$")
|
line_pat = re.compile("^([^ \t]+)[ \t]+(.*)$")
|
||||||
resp, raw_lines = self.longcmd('XGTITLE ' + group)
|
resp, raw_lines = self.longcmd('XGTITLE ' + group)
|
||||||
lines = []
|
lines = []
|
||||||
|
@ -370,13 +368,13 @@ class NNTP:
|
||||||
lines.append(match.group(1, 2))
|
lines.append(match.group(1, 2))
|
||||||
return resp, lines
|
return resp, lines
|
||||||
|
|
||||||
# Process an XPATH command (optional server extension) Arguments:
|
|
||||||
# - id: Message id of article
|
|
||||||
# Returns:
|
|
||||||
# resp: server response if succesful
|
|
||||||
# path: directory path to article
|
|
||||||
|
|
||||||
def xpath(self,id):
|
def xpath(self,id):
|
||||||
|
"""Process an XPATH command (optional server extension) Arguments:
|
||||||
|
- id: Message id of article
|
||||||
|
Returns:
|
||||||
|
resp: server response if succesful
|
||||||
|
path: directory path to article"""
|
||||||
|
|
||||||
resp = self.shortcmd("XPATH " + id)
|
resp = self.shortcmd("XPATH " + id)
|
||||||
if resp[:3] <> '223':
|
if resp[:3] <> '223':
|
||||||
raise error_reply, resp
|
raise error_reply, resp
|
||||||
|
@ -387,14 +385,14 @@ class NNTP:
|
||||||
else:
|
else:
|
||||||
return resp, path
|
return resp, path
|
||||||
|
|
||||||
# Process the DATE command. Arguments:
|
|
||||||
# None
|
|
||||||
# Returns:
|
|
||||||
# resp: server response if succesful
|
|
||||||
# date: Date suitable for newnews/newgroups commands etc.
|
|
||||||
# time: Time suitable for newnews/newgroups commands etc.
|
|
||||||
|
|
||||||
def date (self):
|
def date (self):
|
||||||
|
"""Process the DATE command. Arguments:
|
||||||
|
None
|
||||||
|
Returns:
|
||||||
|
resp: server response if succesful
|
||||||
|
date: Date suitable for newnews/newgroups commands etc.
|
||||||
|
time: Time suitable for newnews/newgroups commands etc."""
|
||||||
|
|
||||||
resp = self.shortcmd("DATE")
|
resp = self.shortcmd("DATE")
|
||||||
if resp[:3] <> '111':
|
if resp[:3] <> '111':
|
||||||
raise error_reply, resp
|
raise error_reply, resp
|
||||||
|
@ -408,12 +406,12 @@ class NNTP:
|
||||||
return resp, date, time
|
return resp, date, time
|
||||||
|
|
||||||
|
|
||||||
# Process a POST command. Arguments:
|
|
||||||
# - f: file containing the article
|
|
||||||
# Returns:
|
|
||||||
# - resp: server response if succesful
|
|
||||||
|
|
||||||
def post(self, f):
|
def post(self, f):
|
||||||
|
"""Process a POST command. Arguments:
|
||||||
|
- f: file containing the article
|
||||||
|
Returns:
|
||||||
|
- resp: server response if succesful"""
|
||||||
|
|
||||||
resp = self.shortcmd('POST')
|
resp = self.shortcmd('POST')
|
||||||
# Raises error_??? if posting is not allowed
|
# Raises error_??? if posting is not allowed
|
||||||
if resp[0] <> '3':
|
if resp[0] <> '3':
|
||||||
|
@ -430,14 +428,14 @@ class NNTP:
|
||||||
self.putline('.')
|
self.putline('.')
|
||||||
return self.getresp()
|
return self.getresp()
|
||||||
|
|
||||||
# Process an IHAVE command. Arguments:
|
|
||||||
# - id: message-id of the article
|
|
||||||
# - f: file containing the article
|
|
||||||
# Returns:
|
|
||||||
# - resp: server response if succesful
|
|
||||||
# Note that if the server refuses the article an exception is raised
|
|
||||||
|
|
||||||
def ihave(self, id, f):
|
def ihave(self, id, f):
|
||||||
|
"""Process an IHAVE command. Arguments:
|
||||||
|
- id: message-id of the article
|
||||||
|
- f: file containing the article
|
||||||
|
Returns:
|
||||||
|
- resp: server response if succesful
|
||||||
|
Note that if the server refuses the article an exception is raised."""
|
||||||
|
|
||||||
resp = self.shortcmd('IHAVE ' + id)
|
resp = self.shortcmd('IHAVE ' + id)
|
||||||
# Raises error_??? if the server already has it
|
# Raises error_??? if the server already has it
|
||||||
if resp[0] <> '3':
|
if resp[0] <> '3':
|
||||||
|
@ -454,10 +452,10 @@ class NNTP:
|
||||||
self.putline('.')
|
self.putline('.')
|
||||||
return self.getresp()
|
return self.getresp()
|
||||||
|
|
||||||
# Process a QUIT command and close the socket. Returns:
|
|
||||||
# - resp: server response if succesful
|
|
||||||
|
|
||||||
def quit(self):
|
def quit(self):
|
||||||
|
"""Process a QUIT command and close the socket. Returns:
|
||||||
|
- resp: server response if succesful"""
|
||||||
|
|
||||||
resp = self.shortcmd('QUIT')
|
resp = self.shortcmd('QUIT')
|
||||||
self.file.close()
|
self.file.close()
|
||||||
self.sock.close()
|
self.sock.close()
|
||||||
|
@ -465,8 +463,8 @@ class NNTP:
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
# Minimal test function
|
|
||||||
def _test():
|
def _test():
|
||||||
|
"""Minimal test function."""
|
||||||
s = NNTP('news')
|
s = NNTP('news')
|
||||||
resp, count, first, last, name = s.group('comp.lang.python')
|
resp, count, first, last, name = s.group('comp.lang.python')
|
||||||
print resp
|
print resp
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
#
|
"""Convert a NT pathname to a file URL and vice versa."""
|
||||||
# nturl2path convert a NT pathname to a file URL and
|
|
||||||
# vice versa
|
|
||||||
|
|
||||||
def url2pathname(url):
|
def url2pathname(url):
|
||||||
""" Convert a URL to a DOS path...
|
""" Convert a URL to a DOS path...
|
||||||
|
@ -34,7 +32,6 @@ def url2pathname(url):
|
||||||
return path
|
return path
|
||||||
|
|
||||||
def pathname2url(p):
|
def pathname2url(p):
|
||||||
|
|
||||||
""" Convert a DOS path name to a file url...
|
""" Convert a DOS path name to a file url...
|
||||||
C:\foo\bar\spam.foo
|
C:\foo\bar\spam.foo
|
||||||
|
|
||||||
|
|
33
Lib/os.py
33
Lib/os.py
|
@ -1,21 +1,22 @@
|
||||||
# os.py -- either mac, dos or posix depending on what system we're on.
|
"""os.py -- either mac, dos or posix depending on what system we're on.
|
||||||
|
|
||||||
# This exports:
|
This exports:
|
||||||
# - all functions from either posix or mac, e.g., os.unlink, os.stat, etc.
|
- all functions from either posix or mac, e.g., os.unlink, os.stat, etc.
|
||||||
# - os.path is either module posixpath or macpath
|
- os.path is either module posixpath or macpath
|
||||||
# - os.name is either 'posix' or 'mac'
|
- os.name is either 'posix' or 'mac'
|
||||||
# - os.curdir is a string representing the current directory ('.' or ':')
|
- os.curdir is a string representing the current directory ('.' or ':')
|
||||||
# - os.pardir is a string representing the parent directory ('..' or '::')
|
- os.pardir is a string representing the parent directory ('..' or '::')
|
||||||
# - os.sep is the (or a most common) pathname separator ('/' or ':' or '\\')
|
- os.sep is the (or a most common) pathname separator ('/' or ':' or '\\')
|
||||||
# - os.altsep is the alternatte pathname separator (None or '/')
|
- os.altsep is the alternatte pathname separator (None or '/')
|
||||||
# - os.pathsep is the component separator used in $PATH etc
|
- os.pathsep is the component separator used in $PATH etc
|
||||||
# - os.defpath is the default search path for executables
|
- os.defpath is the default search path for executables
|
||||||
|
|
||||||
# Programs that import and use 'os' stand a better chance of being
|
Programs that import and use 'os' stand a better chance of being
|
||||||
# portable between different platforms. Of course, they must then
|
portable between different platforms. Of course, they must then
|
||||||
# only use functions that are defined by all platforms (e.g., unlink
|
only use functions that are defined by all platforms (e.g., unlink
|
||||||
# and opendir), and leave all pathname manipulation to os.path
|
and opendir), and leave all pathname manipulation to os.path
|
||||||
# (e.g., split and join).
|
(e.g., split and join).
|
||||||
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
27
Lib/pdb.py
27
Lib/pdb.py
|
@ -1,6 +1,6 @@
|
||||||
#! /usr/bin/env python
|
#! /usr/bin/env python
|
||||||
|
|
||||||
# pdb.py -- finally, a Python debugger!
|
"""pdb.py -- finally, a Python debugger!"""
|
||||||
|
|
||||||
# (See pdb.doc for documentation.)
|
# (See pdb.doc for documentation.)
|
||||||
|
|
||||||
|
@ -106,18 +106,18 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
||||||
# Override Bdb methods (except user_call, for now)
|
# Override Bdb methods (except user_call, for now)
|
||||||
|
|
||||||
def user_line(self, frame):
|
def user_line(self, frame):
|
||||||
# This function is called when we stop or break at this line
|
"""This function is called when we stop or break at this line."""
|
||||||
self.interaction(frame, None)
|
self.interaction(frame, None)
|
||||||
|
|
||||||
def user_return(self, frame, return_value):
|
def user_return(self, frame, return_value):
|
||||||
# This function is called when a return trap is set here
|
"""This function is called when a return trap is set here."""
|
||||||
frame.f_locals['__return__'] = return_value
|
frame.f_locals['__return__'] = return_value
|
||||||
print '--Return--'
|
print '--Return--'
|
||||||
self.interaction(frame, None)
|
self.interaction(frame, None)
|
||||||
|
|
||||||
def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
|
def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
|
||||||
# This function is called if an exception occurs,
|
"""This function is called if an exception occurs,
|
||||||
# but only if we are to stop at or just below this level
|
but only if we are to stop at or just below this level."""
|
||||||
frame.f_locals['__exception__'] = exc_type, exc_value
|
frame.f_locals['__exception__'] = exc_type, exc_value
|
||||||
if type(exc_type) == type(''):
|
if type(exc_type) == type(''):
|
||||||
exc_type_name = exc_type
|
exc_type_name = exc_type
|
||||||
|
@ -148,7 +148,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
||||||
print '***', exc_type_name + ':', v
|
print '***', exc_type_name + ':', v
|
||||||
|
|
||||||
def precmd(self, line):
|
def precmd(self, line):
|
||||||
# Handle alias expansion and ';;' separator
|
"""Handle alias expansion and ';;' separator."""
|
||||||
if not line:
|
if not line:
|
||||||
return line
|
return line
|
||||||
args = string.split(line)
|
args = string.split(line)
|
||||||
|
@ -262,7 +262,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
||||||
|
|
||||||
# To be overridden in derived debuggers
|
# To be overridden in derived debuggers
|
||||||
def defaultFile(self):
|
def defaultFile(self):
|
||||||
# Produce a reasonable default
|
"""Produce a reasonable default."""
|
||||||
filename = self.curframe.f_code.co_filename
|
filename = self.curframe.f_code.co_filename
|
||||||
if filename == '<string>' and mainpyfile:
|
if filename == '<string>' and mainpyfile:
|
||||||
filename = mainpyfile
|
filename = mainpyfile
|
||||||
|
@ -384,7 +384,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
||||||
print 'is now unconditional.'
|
print 'is now unconditional.'
|
||||||
|
|
||||||
def do_ignore(self,arg):
|
def do_ignore(self,arg):
|
||||||
# arg is bp number followed by ignore count
|
"""arg is bp number followed by ignore count."""
|
||||||
args = string.split(arg)
|
args = string.split(arg)
|
||||||
bpnum = int(string.strip(args[0]))
|
bpnum = int(string.strip(args[0]))
|
||||||
try:
|
try:
|
||||||
|
@ -406,10 +406,10 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
||||||
print bpnum, 'is reached.'
|
print bpnum, 'is reached.'
|
||||||
|
|
||||||
def do_clear(self, arg):
|
def do_clear(self, arg):
|
||||||
# Three possibilities, tried in this order:
|
"""Three possibilities, tried in this order:
|
||||||
# clear -> clear all breaks, ask for confirmation
|
clear -> clear all breaks, ask for confirmation
|
||||||
# clear file:lineno -> clear all breaks at file:lineno
|
clear file:lineno -> clear all breaks at file:lineno
|
||||||
# clear bpno bpno ... -> clear breakpoints by number
|
clear bpno bpno ... -> clear breakpoints by number"""
|
||||||
if not arg:
|
if not arg:
|
||||||
try:
|
try:
|
||||||
reply = raw_input('Clear all breaks? ')
|
reply = raw_input('Clear all breaks? ')
|
||||||
|
@ -851,9 +851,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
||||||
def help_pdb(self):
|
def help_pdb(self):
|
||||||
help()
|
help()
|
||||||
|
|
||||||
# Helper function for break/clear parsing -- may be overridden
|
|
||||||
|
|
||||||
def lookupmodule(self, filename):
|
def lookupmodule(self, filename):
|
||||||
|
"""Helper function for break/clear parsing -- may be overridden."""
|
||||||
root, ext = os.path.splitext(filename)
|
root, ext = os.path.splitext(filename)
|
||||||
if ext == '':
|
if ext == '':
|
||||||
filename = filename + '.py'
|
filename = filename + '.py'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
"""create portable serialized representations of Python objects.
|
"""Create portable serialized representations of Python objects.
|
||||||
|
|
||||||
See module cPickle for a (much) faster implementation.
|
See module cPickle for a (much) faster implementation.
|
||||||
See module copy_reg for a mechanism for registering custom picklers.
|
See module copy_reg for a mechanism for registering custom picklers.
|
||||||
|
|
139
Lib/pipes.py
139
Lib/pipes.py
|
@ -1,63 +1,62 @@
|
||||||
# Conversion pipeline templates
|
"""Conversion pipeline templates.
|
||||||
# =============================
|
|
||||||
|
The problem:
|
||||||
|
------------
|
||||||
|
|
||||||
|
Suppose you have some data that you want to convert to another format
|
||||||
|
(e.g. from GIF image format to PPM image format). Maybe the
|
||||||
|
conversion involves several steps (e.g. piping it through compress or
|
||||||
|
uuencode). Some of the conversion steps may require that their input
|
||||||
|
is a disk file, others may be able to read standard input; similar for
|
||||||
|
their output. The input to the entire conversion may also be read
|
||||||
|
from a disk file or from an open file, and similar for its output.
|
||||||
|
|
||||||
|
The module lets you construct a pipeline template by sticking one or
|
||||||
|
more conversion steps together. It will take care of creating and
|
||||||
|
removing temporary files if they are necessary to hold intermediate
|
||||||
|
data. You can then use the template to do conversions from many
|
||||||
|
different sources to many different destinations. The temporary
|
||||||
|
file names used are different each time the template is used.
|
||||||
|
|
||||||
|
The templates are objects so you can create templates for many
|
||||||
|
different conversion steps and store them in a dictionary, for
|
||||||
|
instance.
|
||||||
|
|
||||||
|
|
||||||
# The problem:
|
Directions:
|
||||||
# ------------
|
-----------
|
||||||
#
|
|
||||||
# Suppose you have some data that you want to convert to another format
|
|
||||||
# (e.g. from GIF image format to PPM image format). Maybe the
|
|
||||||
# conversion involves several steps (e.g. piping it through compress or
|
|
||||||
# uuencode). Some of the conversion steps may require that their input
|
|
||||||
# is a disk file, others may be able to read standard input; similar for
|
|
||||||
# their output. The input to the entire conversion may also be read
|
|
||||||
# from a disk file or from an open file, and similar for its output.
|
|
||||||
#
|
|
||||||
# The module lets you construct a pipeline template by sticking one or
|
|
||||||
# more conversion steps together. It will take care of creating and
|
|
||||||
# removing temporary files if they are necessary to hold intermediate
|
|
||||||
# data. You can then use the template to do conversions from many
|
|
||||||
# different sources to many different destinations. The temporary
|
|
||||||
# file names used are different each time the template is used.
|
|
||||||
#
|
|
||||||
# The templates are objects so you can create templates for many
|
|
||||||
# different conversion steps and store them in a dictionary, for
|
|
||||||
# instance.
|
|
||||||
|
|
||||||
|
To create a template:
|
||||||
|
t = Template()
|
||||||
|
|
||||||
# Directions:
|
To add a conversion step to a template:
|
||||||
# -----------
|
t.append(command, kind)
|
||||||
#
|
where kind is a string of two characters: the first is '-' if the
|
||||||
# To create a template:
|
command reads its standard input or 'f' if it requires a file; the
|
||||||
# t = Template()
|
second likewise for the output. The command must be valid /bin/sh
|
||||||
#
|
syntax. If input or output files are required, they are passed as
|
||||||
# To add a conversion step to a template:
|
$IN and $OUT; otherwise, it must be possible to use the command in
|
||||||
# t.append(command, kind)
|
a pipeline.
|
||||||
# where kind is a string of two characters: the first is '-' if the
|
|
||||||
# command reads its standard input or 'f' if it requires a file; the
|
To add a conversion step at the beginning:
|
||||||
# second likewise for the output. The command must be valid /bin/sh
|
t.prepend(command, kind)
|
||||||
# syntax. If input or output files are required, they are passed as
|
|
||||||
# $IN and $OUT; otherwise, it must be possible to use the command in
|
To convert a file to another file using a template:
|
||||||
# a pipeline.
|
sts = t.copy(infile, outfile)
|
||||||
#
|
If infile or outfile are the empty string, standard input is read or
|
||||||
# To add a conversion step at the beginning:
|
standard output is written, respectively. The return value is the
|
||||||
# t.prepend(command, kind)
|
exit status of the conversion pipeline.
|
||||||
#
|
|
||||||
# To convert a file to another file using a template:
|
To open a file for reading or writing through a conversion pipeline:
|
||||||
# sts = t.copy(infile, outfile)
|
fp = t.open(file, mode)
|
||||||
# If infile or outfile are the empty string, standard input is read or
|
where mode is 'r' to read the file, or 'w' to write it -- just like
|
||||||
# standard output is written, respectively. The return value is the
|
for the built-in function open() or for os.popen().
|
||||||
# exit status of the conversion pipeline.
|
|
||||||
#
|
To create a new template object initialized to a given one:
|
||||||
# To open a file for reading or writing through a conversion pipeline:
|
t2 = t.clone()
|
||||||
# fp = t.open(file, mode)
|
|
||||||
# where mode is 'r' to read the file, or 'w' to write it -- just like
|
For an example, see the function test() at the end of the file.
|
||||||
# for the built-in function open() or for os.popen().
|
"""
|
||||||
#
|
|
||||||
# To create a new template object initialized to a given one:
|
|
||||||
# t2 = t.clone()
|
|
||||||
#
|
|
||||||
# For an example, see the function test() at the end of the file.
|
|
||||||
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
@ -81,37 +80,36 @@ stepkinds = [FILEIN_FILEOUT, STDIN_FILEOUT, FILEIN_STDOUT, STDIN_STDOUT, \
|
||||||
SOURCE, SINK]
|
SOURCE, SINK]
|
||||||
|
|
||||||
|
|
||||||
# A pipeline template is a Template object:
|
|
||||||
|
|
||||||
class Template:
|
class Template:
|
||||||
|
"""Class representing a pipeline template."""
|
||||||
|
|
||||||
# Template() returns a fresh pipeline template
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""Template() returns a fresh pipeline template."""
|
||||||
self.debugging = 0
|
self.debugging = 0
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
# t.__repr__() implements `t`
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
"""t.__repr__() implements `t`."""
|
||||||
return '<Template instance, steps=' + `self.steps` + '>'
|
return '<Template instance, steps=' + `self.steps` + '>'
|
||||||
|
|
||||||
# t.reset() restores a pipeline template to its initial state
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
|
"""t.reset() restores a pipeline template to its initial state."""
|
||||||
self.steps = []
|
self.steps = []
|
||||||
|
|
||||||
# t.clone() returns a new pipeline template with identical
|
|
||||||
# initial state as the current one
|
|
||||||
def clone(self):
|
def clone(self):
|
||||||
|
"""t.clone() returns a new pipeline template with identical
|
||||||
|
initial state as the current one."""
|
||||||
t = Template()
|
t = Template()
|
||||||
t.steps = self.steps[:]
|
t.steps = self.steps[:]
|
||||||
t.debugging = self.debugging
|
t.debugging = self.debugging
|
||||||
return t
|
return t
|
||||||
|
|
||||||
# t.debug(flag) turns debugging on or off
|
|
||||||
def debug(self, flag):
|
def debug(self, flag):
|
||||||
|
"""t.debug(flag) turns debugging on or off."""
|
||||||
self.debugging = flag
|
self.debugging = flag
|
||||||
|
|
||||||
# t.append(cmd, kind) adds a new step at the end
|
|
||||||
def append(self, cmd, kind):
|
def append(self, cmd, kind):
|
||||||
|
"""t.append(cmd, kind) adds a new step at the end."""
|
||||||
if type(cmd) <> type(''):
|
if type(cmd) <> type(''):
|
||||||
raise TypeError, \
|
raise TypeError, \
|
||||||
'Template.append: cmd must be a string'
|
'Template.append: cmd must be a string'
|
||||||
|
@ -132,8 +130,8 @@ class Template:
|
||||||
'Template.append: missing $OUT in cmd'
|
'Template.append: missing $OUT in cmd'
|
||||||
self.steps.append((cmd, kind))
|
self.steps.append((cmd, kind))
|
||||||
|
|
||||||
# t.prepend(cmd, kind) adds a new step at the front
|
|
||||||
def prepend(self, cmd, kind):
|
def prepend(self, cmd, kind):
|
||||||
|
"""t.prepend(cmd, kind) adds a new step at the front."""
|
||||||
if type(cmd) <> type(''):
|
if type(cmd) <> type(''):
|
||||||
raise TypeError, \
|
raise TypeError, \
|
||||||
'Template.prepend: cmd must be a string'
|
'Template.prepend: cmd must be a string'
|
||||||
|
@ -154,9 +152,9 @@ class Template:
|
||||||
'Template.prepend: missing $OUT in cmd'
|
'Template.prepend: missing $OUT in cmd'
|
||||||
self.steps.insert(0, (cmd, kind))
|
self.steps.insert(0, (cmd, kind))
|
||||||
|
|
||||||
# t.open(file, rw) returns a pipe or file object open for
|
|
||||||
# reading or writing; the file is the other end of the pipeline
|
|
||||||
def open(self, file, rw):
|
def open(self, file, rw):
|
||||||
|
"""t.open(file, rw) returns a pipe or file object open for
|
||||||
|
reading or writing; the file is the other end of the pipeline."""
|
||||||
if rw == 'r':
|
if rw == 'r':
|
||||||
return self.open_r(file)
|
return self.open_r(file)
|
||||||
if rw == 'w':
|
if rw == 'w':
|
||||||
|
@ -164,10 +162,9 @@ class Template:
|
||||||
raise ValueError, \
|
raise ValueError, \
|
||||||
'Template.open: rw must be \'r\' or \'w\', not ' + `rw`
|
'Template.open: rw must be \'r\' or \'w\', not ' + `rw`
|
||||||
|
|
||||||
# t.open_r(file) and t.open_w(file) implement
|
|
||||||
# t.open(file, 'r') and t.open(file, 'w') respectively
|
|
||||||
|
|
||||||
def open_r(self, file):
|
def open_r(self, file):
|
||||||
|
"""t.open_r(file) and t.open_w(file) implement
|
||||||
|
t.open(file, 'r') and t.open(file, 'w') respectively."""
|
||||||
if self.steps == []:
|
if self.steps == []:
|
||||||
return open(file, 'r')
|
return open(file, 'r')
|
||||||
if self.steps[-1][1] == SINK:
|
if self.steps[-1][1] == SINK:
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
"""Spawn a command with pipes to its stdin, stdout, and optionally stderr.
|
||||||
|
|
||||||
|
The normal os.popen(cmd, mode) call spawns a shell command and provides a
|
||||||
|
file interface to just the input or output of the process depending on
|
||||||
|
whether mode is 'r' or 'w'. This module provides the functions popen2(cmd)
|
||||||
|
and popen3(cmd) which return two or three pipes to the spawned command.
|
||||||
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import string
|
import string
|
||||||
|
@ -11,7 +19,15 @@ def _cleanup():
|
||||||
inst.poll()
|
inst.poll()
|
||||||
|
|
||||||
class Popen3:
|
class Popen3:
|
||||||
|
"""Class representing a child process. Normally instances are created
|
||||||
|
by the factory functions popen2() and popen3()."""
|
||||||
|
|
||||||
def __init__(self, cmd, capturestderr=0, bufsize=-1):
|
def __init__(self, cmd, capturestderr=0, bufsize=-1):
|
||||||
|
"""The parameter 'cmd' is the shell command to execute in a
|
||||||
|
sub-process. The 'capturestderr' flag, if true, specifies that
|
||||||
|
the object should capture standard error output of the child process.
|
||||||
|
The default is false. If the 'bufsize' parameter is specified, it
|
||||||
|
specifies the size of the I/O buffers to/from the child process."""
|
||||||
if type(cmd) == type(''):
|
if type(cmd) == type(''):
|
||||||
cmd = ['/bin/sh', '-c', cmd]
|
cmd = ['/bin/sh', '-c', cmd]
|
||||||
p2cread, p2cwrite = os.pipe()
|
p2cread, p2cwrite = os.pipe()
|
||||||
|
@ -51,7 +67,10 @@ class Popen3:
|
||||||
self.childerr = None
|
self.childerr = None
|
||||||
self.sts = -1 # Child not completed yet
|
self.sts = -1 # Child not completed yet
|
||||||
_active.append(self)
|
_active.append(self)
|
||||||
|
|
||||||
def poll(self):
|
def poll(self):
|
||||||
|
"""Return the exit status of the child process if it has finished,
|
||||||
|
or -1 if it hasn't finished yet."""
|
||||||
if self.sts < 0:
|
if self.sts < 0:
|
||||||
try:
|
try:
|
||||||
pid, sts = os.waitpid(self.pid, os.WNOHANG)
|
pid, sts = os.waitpid(self.pid, os.WNOHANG)
|
||||||
|
@ -61,7 +80,9 @@ class Popen3:
|
||||||
except os.error:
|
except os.error:
|
||||||
pass
|
pass
|
||||||
return self.sts
|
return self.sts
|
||||||
|
|
||||||
def wait(self):
|
def wait(self):
|
||||||
|
"""Wait for and return the exit status of the child process."""
|
||||||
pid, sts = os.waitpid(self.pid, 0)
|
pid, sts = os.waitpid(self.pid, 0)
|
||||||
if pid == self.pid:
|
if pid == self.pid:
|
||||||
self.sts = sts
|
self.sts = sts
|
||||||
|
@ -69,11 +90,17 @@ class Popen3:
|
||||||
return self.sts
|
return self.sts
|
||||||
|
|
||||||
def popen2(cmd, bufsize=-1):
|
def popen2(cmd, bufsize=-1):
|
||||||
|
"""Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
|
||||||
|
specified, it sets the buffer size for the I/O pipes. The file objects
|
||||||
|
(child_stdout, child_stdin) are returned."""
|
||||||
_cleanup()
|
_cleanup()
|
||||||
inst = Popen3(cmd, 0, bufsize)
|
inst = Popen3(cmd, 0, bufsize)
|
||||||
return inst.fromchild, inst.tochild
|
return inst.fromchild, inst.tochild
|
||||||
|
|
||||||
def popen3(cmd, bufsize=-1):
|
def popen3(cmd, bufsize=-1):
|
||||||
|
"""Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
|
||||||
|
specified, it sets the buffer size for the I/O pipes. The file objects
|
||||||
|
(child_stdout, child_stdin, child_stderr) are returned."""
|
||||||
_cleanup()
|
_cleanup()
|
||||||
inst = Popen3(cmd, 1, bufsize)
|
inst = Popen3(cmd, 1, bufsize)
|
||||||
return inst.fromchild, inst.tochild, inst.childerr
|
return inst.fromchild, inst.tochild, inst.childerr
|
||||||
|
|
118
Lib/posixfile.py
118
Lib/posixfile.py
|
@ -1,64 +1,61 @@
|
||||||
#
|
"""Extended file operations available in POSIX.
|
||||||
# Start of posixfile.py
|
|
||||||
#
|
|
||||||
|
|
||||||
#
|
f = posixfile.open(filename, [mode, [bufsize]])
|
||||||
# Extended file operations
|
will create a new posixfile object
|
||||||
#
|
|
||||||
# f = posixfile.open(filename, [mode, [bufsize]])
|
f = posixfile.fileopen(fileobject)
|
||||||
# will create a new posixfile object
|
will create a posixfile object from a builtin file object
|
||||||
#
|
|
||||||
# f = posixfile.fileopen(fileobject)
|
f.file()
|
||||||
# will create a posixfile object from a builtin file object
|
will return the original builtin file object
|
||||||
#
|
|
||||||
# f.file()
|
f.dup()
|
||||||
# will return the original builtin file object
|
will return a new file object based on a new filedescriptor
|
||||||
#
|
|
||||||
# f.dup()
|
f.dup2(fd)
|
||||||
# will return a new file object based on a new filedescriptor
|
will return a new file object based on the given filedescriptor
|
||||||
#
|
|
||||||
# f.dup2(fd)
|
f.flags(mode)
|
||||||
# will return a new file object based on the given filedescriptor
|
will turn on the associated flag (merge)
|
||||||
#
|
mode can contain the following characters:
|
||||||
# f.flags(mode)
|
|
||||||
# will turn on the associated flag (merge)
|
(character representing a flag)
|
||||||
# mode can contain the following characters:
|
a append only flag
|
||||||
#
|
c close on exec flag
|
||||||
# (character representing a flag)
|
n no delay flag
|
||||||
# a append only flag
|
s synchronization flag
|
||||||
# c close on exec flag
|
(modifiers)
|
||||||
# n no delay flag
|
! turn flags 'off' instead of default 'on'
|
||||||
# s synchronization flag
|
= copy flags 'as is' instead of default 'merge'
|
||||||
# (modifiers)
|
? return a string in which the characters represent the flags
|
||||||
# ! turn flags 'off' instead of default 'on'
|
that are set
|
||||||
# = copy flags 'as is' instead of default 'merge'
|
|
||||||
# ? return a string in which the characters represent the flags
|
note: - the '!' and '=' modifiers are mutually exclusive.
|
||||||
# that are set
|
- the '?' modifier will return the status of the flags after they
|
||||||
#
|
have been changed by other characters in the mode string
|
||||||
# note: - the '!' and '=' modifiers are mutually exclusive.
|
|
||||||
# - the '?' modifier will return the status of the flags after they
|
f.lock(mode [, len [, start [, whence]]])
|
||||||
# have been changed by other characters in the mode string
|
will (un)lock a region
|
||||||
#
|
mode can contain the following characters:
|
||||||
# f.lock(mode [, len [, start [, whence]]])
|
|
||||||
# will (un)lock a region
|
(character representing type of lock)
|
||||||
# mode can contain the following characters:
|
u unlock
|
||||||
#
|
r read lock
|
||||||
# (character representing type of lock)
|
w write lock
|
||||||
# u unlock
|
(modifiers)
|
||||||
# r read lock
|
| wait until the lock can be granted
|
||||||
# w write lock
|
? return the first lock conflicting with the requested lock
|
||||||
# (modifiers)
|
or 'None' if there is no conflict. The lock returned is in the
|
||||||
# | wait until the lock can be granted
|
format (mode, len, start, whence, pid) where mode is a
|
||||||
# ? return the first lock conflicting with the requested lock
|
character representing the type of lock ('r' or 'w')
|
||||||
# or 'None' if there is no conflict. The lock returned is in the
|
|
||||||
# format (mode, len, start, whence, pid) where mode is a
|
note: - the '?' modifier prevents a region from being locked; it is
|
||||||
# character representing the type of lock ('r' or 'w')
|
query only
|
||||||
#
|
"""
|
||||||
# note: - the '?' modifier prevents a region from being locked; it is
|
|
||||||
# query only
|
|
||||||
#
|
|
||||||
|
|
||||||
class _posixfile_:
|
class _posixfile_:
|
||||||
|
"""File wrapper class that provides extra POSIX file routines."""
|
||||||
|
|
||||||
states = ['open', 'closed']
|
states = ['open', 'closed']
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -215,13 +212,12 @@ class _posixfile_:
|
||||||
else:
|
else:
|
||||||
return 'w', l_len, l_start, l_whence, l_pid
|
return 'w', l_len, l_start, l_whence, l_pid
|
||||||
|
|
||||||
#
|
|
||||||
# Public routine to obtain a posixfile object
|
|
||||||
#
|
|
||||||
def open(name, mode='r', bufsize=-1):
|
def open(name, mode='r', bufsize=-1):
|
||||||
|
"""Public routine to open a file as a posixfile object."""
|
||||||
return _posixfile_().open(name, mode, bufsize)
|
return _posixfile_().open(name, mode, bufsize)
|
||||||
|
|
||||||
def fileopen(file):
|
def fileopen(file):
|
||||||
|
"""Public routine to get a posixfile object from a Python file object."""
|
||||||
return _posixfile_().fileopen(file)
|
return _posixfile_().fileopen(file)
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
# Module 'posixpath' -- common operations on Posix pathnames.
|
"""Common operations on Posix pathnames.
|
||||||
# Some of this can actually be useful on non-Posix systems too, e.g.
|
|
||||||
# for manipulation of the pathname component of URLs.
|
Instead of importing this module directly, import os and refer to
|
||||||
# The "os.path" name is an alias for this module on Posix systems;
|
this module as os.path. The "os.path" name is an alias for this
|
||||||
# on other systems (e.g. Mac, Windows), os.path provides the same
|
module on Posix systems; on other systems (e.g. Mac, Windows),
|
||||||
# operations in a manner specific to that platform, and is an alias
|
os.path provides the same operations in a manner specific to that
|
||||||
# to another module (e.g. macpath, ntpath).
|
platform, and is an alias to another module (e.g. macpath, ntpath).
|
||||||
"""Common pathname manipulations, Posix version.
|
|
||||||
Instead of importing this module
|
Some of this can actually be useful on non-Posix systems too, e.g.
|
||||||
directly, import os and refer to this module as os.path.
|
for manipulation of the pathname component of URLs.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -369,8 +369,8 @@ def normpath(path):
|
||||||
return slashes + string.joinfields(comps, '/')
|
return slashes + string.joinfields(comps, '/')
|
||||||
|
|
||||||
|
|
||||||
# Return an absolute path.
|
|
||||||
def abspath(path):
|
def abspath(path):
|
||||||
|
"""Return an absolute path."""
|
||||||
if not isabs(path):
|
if not isabs(path):
|
||||||
path = join(os.getcwd(), path)
|
path = join(os.getcwd(), path)
|
||||||
return normpath(path)
|
return normpath(path)
|
||||||
|
|
110
Lib/profile.py
110
Lib/profile.py
|
@ -7,6 +7,7 @@
|
||||||
#
|
#
|
||||||
# See profile.doc for more information
|
# See profile.doc for more information
|
||||||
|
|
||||||
|
"""Class for profiling Python code."""
|
||||||
|
|
||||||
# Copyright 1994, by InfoSeek Corporation, all rights reserved.
|
# Copyright 1994, by InfoSeek Corporation, all rights reserved.
|
||||||
# Written by James Roskind
|
# Written by James Roskind
|
||||||
|
@ -79,44 +80,43 @@ def help():
|
||||||
print 'along the Python search path'
|
print 'along the Python search path'
|
||||||
|
|
||||||
|
|
||||||
#**************************************************************************
|
|
||||||
# class Profile documentation:
|
|
||||||
#**************************************************************************
|
|
||||||
# self.cur is always a tuple. Each such tuple corresponds to a stack
|
|
||||||
# frame that is currently active (self.cur[-2]). The following are the
|
|
||||||
# definitions of its members. We use this external "parallel stack" to
|
|
||||||
# avoid contaminating the program that we are profiling. (old profiler
|
|
||||||
# used to write into the frames local dictionary!!) Derived classes
|
|
||||||
# can change the definition of some entries, as long as they leave
|
|
||||||
# [-2:] intact.
|
|
||||||
#
|
|
||||||
# [ 0] = Time that needs to be charged to the parent frame's function. It is
|
|
||||||
# used so that a function call will not have to access the timing data
|
|
||||||
# for the parents frame.
|
|
||||||
# [ 1] = Total time spent in this frame's function, excluding time in
|
|
||||||
# subfunctions
|
|
||||||
# [ 2] = Cumulative time spent in this frame's function, including time in
|
|
||||||
# all subfunctions to this frame.
|
|
||||||
# [-3] = Name of the function that corresonds to this frame.
|
|
||||||
# [-2] = Actual frame that we correspond to (used to sync exception handling)
|
|
||||||
# [-1] = Our parent 6-tuple (corresonds to frame.f_back)
|
|
||||||
#**************************************************************************
|
|
||||||
# Timing data for each function is stored as a 5-tuple in the dictionary
|
|
||||||
# self.timings[]. The index is always the name stored in self.cur[4].
|
|
||||||
# The following are the definitions of the members:
|
|
||||||
#
|
|
||||||
# [0] = The number of times this function was called, not counting direct
|
|
||||||
# or indirect recursion,
|
|
||||||
# [1] = Number of times this function appears on the stack, minus one
|
|
||||||
# [2] = Total time spent internal to this function
|
|
||||||
# [3] = Cumulative time that this function was present on the stack. In
|
|
||||||
# non-recursive functions, this is the total execution time from start
|
|
||||||
# to finish of each invocation of a function, including time spent in
|
|
||||||
# all subfunctions.
|
|
||||||
# [5] = A dictionary indicating for each function name, the number of times
|
|
||||||
# it was called by us.
|
|
||||||
#**************************************************************************
|
|
||||||
class Profile:
|
class Profile:
|
||||||
|
"""Profiler class.
|
||||||
|
|
||||||
|
self.cur is always a tuple. Each such tuple corresponds to a stack
|
||||||
|
frame that is currently active (self.cur[-2]). The following are the
|
||||||
|
definitions of its members. We use this external "parallel stack" to
|
||||||
|
avoid contaminating the program that we are profiling. (old profiler
|
||||||
|
used to write into the frames local dictionary!!) Derived classes
|
||||||
|
can change the definition of some entries, as long as they leave
|
||||||
|
[-2:] intact.
|
||||||
|
|
||||||
|
[ 0] = Time that needs to be charged to the parent frame's function.
|
||||||
|
It is used so that a function call will not have to access the
|
||||||
|
timing data for the parent frame.
|
||||||
|
[ 1] = Total time spent in this frame's function, excluding time in
|
||||||
|
subfunctions
|
||||||
|
[ 2] = Cumulative time spent in this frame's function, including time in
|
||||||
|
all subfunctions to this frame.
|
||||||
|
[-3] = Name of the function that corresonds to this frame.
|
||||||
|
[-2] = Actual frame that we correspond to (used to sync exception handling)
|
||||||
|
[-1] = Our parent 6-tuple (corresonds to frame.f_back)
|
||||||
|
|
||||||
|
Timing data for each function is stored as a 5-tuple in the dictionary
|
||||||
|
self.timings[]. The index is always the name stored in self.cur[4].
|
||||||
|
The following are the definitions of the members:
|
||||||
|
|
||||||
|
[0] = The number of times this function was called, not counting direct
|
||||||
|
or indirect recursion,
|
||||||
|
[1] = Number of times this function appears on the stack, minus one
|
||||||
|
[2] = Total time spent internal to this function
|
||||||
|
[3] = Cumulative time that this function was present on the stack. In
|
||||||
|
non-recursive functions, this is the total execution time from start
|
||||||
|
to finish of each invocation of a function, including time spent in
|
||||||
|
all subfunctions.
|
||||||
|
[5] = A dictionary indicating for each function name, the number of times
|
||||||
|
it was called by us.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, timer=None):
|
def __init__(self, timer=None):
|
||||||
self.timings = {}
|
self.timings = {}
|
||||||
|
@ -449,19 +449,16 @@ class Profile:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#****************************************************************************
|
|
||||||
# OldProfile class documentation
|
|
||||||
#****************************************************************************
|
|
||||||
#
|
|
||||||
# The following derived profiler simulates the old style profile, providing
|
|
||||||
# errant results on recursive functions. The reason for the usefulnes of this
|
|
||||||
# profiler is that it runs faster (i.e., less overhead). It still creates
|
|
||||||
# all the caller stats, and is quite useful when there is *no* recursion
|
|
||||||
# in the user's code.
|
|
||||||
#
|
|
||||||
# This code also shows how easy it is to create a modified profiler.
|
|
||||||
#****************************************************************************
|
|
||||||
class OldProfile(Profile):
|
class OldProfile(Profile):
|
||||||
|
"""A derived profiler that simulates the old style profile, providing
|
||||||
|
errant results on recursive functions. The reason for the usefulness of
|
||||||
|
this profiler is that it runs faster (i.e., less overhead). It still
|
||||||
|
creates all the caller stats, and is quite useful when there is *no*
|
||||||
|
recursion in the user's code.
|
||||||
|
|
||||||
|
This code also shows how easy it is to create a modified profiler.
|
||||||
|
"""
|
||||||
|
|
||||||
def trace_dispatch_exception(self, frame, t):
|
def trace_dispatch_exception(self, frame, t):
|
||||||
rt, rtt, rct, rfn, rframe, rcur = self.cur
|
rt, rtt, rct, rfn, rframe, rcur = self.cur
|
||||||
if rcur and not rframe is frame:
|
if rcur and not rframe is frame:
|
||||||
|
@ -509,16 +506,13 @@ class OldProfile(Profile):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#****************************************************************************
|
|
||||||
# HotProfile class documentation
|
|
||||||
#****************************************************************************
|
|
||||||
#
|
|
||||||
# This profiler is the fastest derived profile example. It does not
|
|
||||||
# calculate caller-callee relationships, and does not calculate cumulative
|
|
||||||
# time under a function. It only calculates time spent in a function, so
|
|
||||||
# it runs very quickly (re: very low overhead)
|
|
||||||
#****************************************************************************
|
|
||||||
class HotProfile(Profile):
|
class HotProfile(Profile):
|
||||||
|
"""The fastest derived profile example. It does not calculate
|
||||||
|
caller-callee relationships, and does not calculate cumulative
|
||||||
|
time under a function. It only calculates time spent in a
|
||||||
|
function, so it runs very quickly due to its very low overhead.
|
||||||
|
"""
|
||||||
|
|
||||||
def trace_dispatch_exception(self, frame, t):
|
def trace_dispatch_exception(self, frame, t):
|
||||||
rt, rtt, rfn, rframe, rcur = self.cur
|
rt, rtt, rfn, rframe, rcur = self.cur
|
||||||
if rcur and not rframe is frame:
|
if rcur and not rframe is frame:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#
|
"""Class for printing reports on profiled python code."""
|
||||||
|
|
||||||
# Class for printing reports on profiled python code. rev 1.0 4/1/94
|
# Class for printing reports on profiled python code. rev 1.0 4/1/94
|
||||||
#
|
#
|
||||||
# Based on prior profile module by Sjoerd Mullender...
|
# Based on prior profile module by Sjoerd Mullender...
|
||||||
|
@ -37,41 +38,38 @@ import string
|
||||||
import marshal
|
import marshal
|
||||||
import re
|
import re
|
||||||
|
|
||||||
#**************************************************************************
|
|
||||||
# Class Stats documentation
|
|
||||||
#**************************************************************************
|
|
||||||
# This class is used for creating reports from data generated by the
|
|
||||||
# Profile class. It is a "friend" of that class, and imports data either
|
|
||||||
# by direct access to members of Profile class, or by reading in a dictionary
|
|
||||||
# that was emitted (via marshal) from the Profile class.
|
|
||||||
#
|
|
||||||
# The big change from the previous Profiler (in terms of raw functionality)
|
|
||||||
# is that an "add()" method has been provided to combine Stats from
|
|
||||||
# several distinct profile runs. Both the constructor and the add()
|
|
||||||
# method now take arbitrarilly many file names as arguments.
|
|
||||||
#
|
|
||||||
# All the print methods now take an argument that indicats how many lines
|
|
||||||
# to print. If the arg is a floating point number between 0 and 1.0, then
|
|
||||||
# it is taken as a decimal percentage of the availabel lines to be printed
|
|
||||||
# (e.g., .1 means print 10% of all available lines). If it is an integer,
|
|
||||||
# it is taken to mean the number of lines of data that you wish to have
|
|
||||||
# printed.
|
|
||||||
#
|
|
||||||
# The sort_stats() method now processes some additionaly options (i.e., in
|
|
||||||
# addition to the old -1, 0, 1, or 2). It takes an arbitrary number of quoted
|
|
||||||
# strings to select the sort order. For example sort_stats('time', 'name')
|
|
||||||
# sorts on the major key of "internal function time", and on the minor
|
|
||||||
# key of 'the name of the function'. Look at the two tables in sort_stats()
|
|
||||||
# and get_sort_arg_defs(self) for more examples.
|
|
||||||
#
|
|
||||||
# All methods now return "self", so you can string together commands like:
|
|
||||||
# Stats('foo', 'goo').strip_dirs().sort_stats('calls').\
|
|
||||||
# print_stats(5).print_callers(5)
|
|
||||||
#
|
|
||||||
#**************************************************************************
|
|
||||||
import fpformat
|
import fpformat
|
||||||
|
|
||||||
class Stats:
|
class Stats:
|
||||||
|
"""This class is used for creating reports from data generated by the
|
||||||
|
Profile class. It is a "friend" of that class, and imports data either
|
||||||
|
by direct access to members of Profile class, or by reading in a dictionary
|
||||||
|
that was emitted (via marshal) from the Profile class.
|
||||||
|
|
||||||
|
The big change from the previous Profiler (in terms of raw functionality)
|
||||||
|
is that an "add()" method has been provided to combine Stats from
|
||||||
|
several distinct profile runs. Both the constructor and the add()
|
||||||
|
method now take arbitrarilly many file names as arguments.
|
||||||
|
|
||||||
|
All the print methods now take an argument that indicats how many lines
|
||||||
|
to print. If the arg is a floating point number between 0 and 1.0, then
|
||||||
|
it is taken as a decimal percentage of the availabel lines to be printed
|
||||||
|
(e.g., .1 means print 10% of all available lines). If it is an integer,
|
||||||
|
it is taken to mean the number of lines of data that you wish to have
|
||||||
|
printed.
|
||||||
|
|
||||||
|
The sort_stats() method now processes some additionaly options (i.e., in
|
||||||
|
addition to the old -1, 0, 1, or 2). It takes an arbitrary number of quoted
|
||||||
|
strings to select the sort order. For example sort_stats('time', 'name')
|
||||||
|
sorts on the major key of "internal function time", and on the minor
|
||||||
|
key of 'the name of the function'. Look at the two tables in sort_stats()
|
||||||
|
and get_sort_arg_defs(self) for more examples.
|
||||||
|
|
||||||
|
All methods now return "self", so you can string together commands like:
|
||||||
|
Stats('foo', 'goo').strip_dirs().sort_stats('calls').\
|
||||||
|
print_stats(5).print_callers(5)
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
if not len(args):
|
if not len(args):
|
||||||
arg = None
|
arg = None
|
||||||
|
@ -182,8 +180,8 @@ class Stats:
|
||||||
"time" : (((2,-1), ), "internal time"),\
|
"time" : (((2,-1), ), "internal time"),\
|
||||||
}
|
}
|
||||||
|
|
||||||
# Expand all abbreviations that are unique
|
|
||||||
def get_sort_arg_defs(self):
|
def get_sort_arg_defs(self):
|
||||||
|
"""Expand all abbreviations that are unique."""
|
||||||
if not self.sort_arg_dict:
|
if not self.sort_arg_dict:
|
||||||
self.sort_arg_dict = dict = {}
|
self.sort_arg_dict = dict = {}
|
||||||
std_list = dict.keys()
|
std_list = dict.keys()
|
||||||
|
@ -289,9 +287,9 @@ class Stats:
|
||||||
all_callees[func2][func] = callers[func2]
|
all_callees[func2][func] = callers[func2]
|
||||||
return
|
return
|
||||||
|
|
||||||
#******************************************************************
|
#******************************************************************
|
||||||
# The following functions support actual printing of reports
|
# The following functions support actual printing of reports
|
||||||
#******************************************************************
|
#******************************************************************
|
||||||
|
|
||||||
# Optional "amount" is either a line count, or a percentage of lines.
|
# Optional "amount" is either a line count, or a percentage of lines.
|
||||||
|
|
||||||
|
@ -447,17 +445,14 @@ class Stats:
|
||||||
pass # has no return value, so use at end of line :-)
|
pass # has no return value, so use at end of line :-)
|
||||||
|
|
||||||
|
|
||||||
#**************************************************************************
|
|
||||||
# class TupleComp Documentation
|
|
||||||
#**************************************************************************
|
|
||||||
# This class provides a generic function for comparing any two tuples.
|
|
||||||
# Each instance records a list of tuple-indicies (from most significant
|
|
||||||
# to least significant), and sort direction (ascending or decending) for
|
|
||||||
# each tuple-index. The compare functions can then be used as the function
|
|
||||||
# argument to the system sort() function when a list of tuples need to be
|
|
||||||
# sorted in the instances order.
|
|
||||||
#**************************************************************************
|
|
||||||
class TupleComp:
|
class TupleComp:
|
||||||
|
"""This class provides a generic function for comparing any two tuples.
|
||||||
|
Each instance records a list of tuple-indicies (from most significant
|
||||||
|
to least significant), and sort direction (ascending or decending) for
|
||||||
|
each tuple-index. The compare functions can then be used as the function
|
||||||
|
argument to the system sort() function when a list of tuples need to be
|
||||||
|
sorted in the instances order."""
|
||||||
|
|
||||||
def __init__(self, comp_select_list):
|
def __init__(self, comp_select_list):
|
||||||
self.comp_select_list = comp_select_list
|
self.comp_select_list = comp_select_list
|
||||||
|
|
||||||
|
@ -495,16 +490,16 @@ def func_split(func_name):
|
||||||
# such as callers and callees.
|
# such as callers and callees.
|
||||||
#**************************************************************************
|
#**************************************************************************
|
||||||
|
|
||||||
# Add together all the stats for two profile entries
|
def add_func_stats(target, source):
|
||||||
def add_func_stats(target, source):
|
"""Add together all the stats for two profile entries."""
|
||||||
cc, nc, tt, ct, callers = source
|
cc, nc, tt, ct, callers = source
|
||||||
t_cc, t_nc, t_tt, t_ct, t_callers = target
|
t_cc, t_nc, t_tt, t_ct, t_callers = target
|
||||||
return (cc+t_cc, nc+t_nc, tt+t_tt, ct+t_ct, \
|
return (cc+t_cc, nc+t_nc, tt+t_tt, ct+t_ct, \
|
||||||
add_callers(t_callers, callers))
|
add_callers(t_callers, callers))
|
||||||
|
|
||||||
|
|
||||||
# Combine two caller lists in a single list.
|
|
||||||
def add_callers(target, source):
|
def add_callers(target, source):
|
||||||
|
"""Combine two caller lists in a single list."""
|
||||||
new_callers = {}
|
new_callers = {}
|
||||||
for func in target.keys():
|
for func in target.keys():
|
||||||
new_callers[func] = target[func]
|
new_callers[func] = target[func]
|
||||||
|
@ -515,8 +510,8 @@ def add_callers(target, source):
|
||||||
new_callers[func] = source[func]
|
new_callers[func] = source[func]
|
||||||
return new_callers
|
return new_callers
|
||||||
|
|
||||||
# Sum the caller statistics to get total number of calls recieved
|
|
||||||
def count_calls(callers):
|
def count_calls(callers):
|
||||||
|
"""Sum the caller statistics to get total number of calls received."""
|
||||||
nc = 0
|
nc = 0
|
||||||
for func in callers.keys():
|
for func in callers.keys():
|
||||||
nc = nc + callers[func]
|
nc = nc + callers[func]
|
||||||
|
@ -529,4 +524,3 @@ def count_calls(callers):
|
||||||
def f8(x):
|
def f8(x):
|
||||||
return string.rjust(fpformat.fix(x, 3), 8)
|
return string.rjust(fpformat.fix(x, 3), 8)
|
||||||
|
|
||||||
|
|
||||||
|
|
28
Lib/pty.py
28
Lib/pty.py
|
@ -1,4 +1,4 @@
|
||||||
# pty.py -- Pseudo terminal utilities.
|
"""Pseudo terminal utilities."""
|
||||||
|
|
||||||
# Bugs: No signal handling. Doesn't set slave termios and window size.
|
# Bugs: No signal handling. Doesn't set slave termios and window size.
|
||||||
# Only tested on Linux.
|
# Only tested on Linux.
|
||||||
|
@ -16,8 +16,9 @@ STDERR_FILENO = 2
|
||||||
|
|
||||||
CHILD = 0
|
CHILD = 0
|
||||||
|
|
||||||
# Open pty master. Returns (master_fd, tty_name). SGI and Linux/BSD version.
|
|
||||||
def master_open():
|
def master_open():
|
||||||
|
"""Open pty master and return (master_fd, tty_name).
|
||||||
|
SGI and Linux/BSD version."""
|
||||||
try:
|
try:
|
||||||
import sgi
|
import sgi
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -38,14 +39,15 @@ def master_open():
|
||||||
return (fd, '/dev/tty' + x + y)
|
return (fd, '/dev/tty' + x + y)
|
||||||
raise os.error, 'out of pty devices'
|
raise os.error, 'out of pty devices'
|
||||||
|
|
||||||
# Open the pty slave. Acquire the controlling terminal.
|
|
||||||
# Returns file descriptor. Linux version. (Should be universal? --Guido)
|
|
||||||
def slave_open(tty_name):
|
def slave_open(tty_name):
|
||||||
|
"""Open the pty slave and acquire the controlling terminal.
|
||||||
|
Return the file descriptor. Linux version."""
|
||||||
|
# (Should be universal? --Guido)
|
||||||
return os.open(tty_name, FCNTL.O_RDWR)
|
return os.open(tty_name, FCNTL.O_RDWR)
|
||||||
|
|
||||||
# Fork and make the child a session leader with a controlling terminal.
|
|
||||||
# Returns (pid, master_fd)
|
|
||||||
def fork():
|
def fork():
|
||||||
|
"""Fork and make the child a session leader with a controlling terminal.
|
||||||
|
Return (pid, master_fd)."""
|
||||||
master_fd, tty_name = master_open()
|
master_fd, tty_name = master_open()
|
||||||
pid = os.fork()
|
pid = os.fork()
|
||||||
if pid == CHILD:
|
if pid == CHILD:
|
||||||
|
@ -66,21 +68,21 @@ def fork():
|
||||||
# Parent and child process.
|
# Parent and child process.
|
||||||
return pid, master_fd
|
return pid, master_fd
|
||||||
|
|
||||||
# Write all the data to a descriptor.
|
|
||||||
def writen(fd, data):
|
def writen(fd, data):
|
||||||
|
"""Write all the data to a descriptor."""
|
||||||
while data != '':
|
while data != '':
|
||||||
n = os.write(fd, data)
|
n = os.write(fd, data)
|
||||||
data = data[n:]
|
data = data[n:]
|
||||||
|
|
||||||
# Default read function.
|
|
||||||
def read(fd):
|
def read(fd):
|
||||||
|
"""Default read function."""
|
||||||
return os.read(fd, 1024)
|
return os.read(fd, 1024)
|
||||||
|
|
||||||
# Parent copy loop.
|
|
||||||
# Copies
|
|
||||||
# pty master -> standard output (master_read)
|
|
||||||
# standard input -> pty master (stdin_read)
|
|
||||||
def copy(master_fd, master_read=read, stdin_read=read):
|
def copy(master_fd, master_read=read, stdin_read=read):
|
||||||
|
"""Parent copy loop.
|
||||||
|
Copies
|
||||||
|
pty master -> standard output (master_read)
|
||||||
|
standard input -> pty master (stdin_read)"""
|
||||||
while 1:
|
while 1:
|
||||||
rfds, wfds, xfds = select(
|
rfds, wfds, xfds = select(
|
||||||
[master_fd, STDIN_FILENO], [], [])
|
[master_fd, STDIN_FILENO], [], [])
|
||||||
|
@ -91,8 +93,8 @@ def copy(master_fd, master_read=read, stdin_read=read):
|
||||||
data = stdin_read(STDIN_FILENO)
|
data = stdin_read(STDIN_FILENO)
|
||||||
writen(master_fd, data)
|
writen(master_fd, data)
|
||||||
|
|
||||||
# Create a spawned process.
|
|
||||||
def spawn(argv, master_read=read, stdin_read=read):
|
def spawn(argv, master_read=read, stdin_read=read):
|
||||||
|
"""Create a spawned process."""
|
||||||
if type(argv) == type(''):
|
if type(argv) == type(''):
|
||||||
argv = (argv,)
|
argv = (argv,)
|
||||||
pid, master_fd = fork()
|
pid, master_fd = fork()
|
||||||
|
|
|
@ -7,7 +7,7 @@ import imp
|
||||||
MAGIC = imp.get_magic()
|
MAGIC = imp.get_magic()
|
||||||
|
|
||||||
def wr_long(f, x):
|
def wr_long(f, x):
|
||||||
"Internal; write a 32-bit int to a file in little-endian order."
|
"""Internal; write a 32-bit int to a file in little-endian order."""
|
||||||
f.write(chr( x & 0xff))
|
f.write(chr( x & 0xff))
|
||||||
f.write(chr((x >> 8) & 0xff))
|
f.write(chr((x >> 8) & 0xff))
|
||||||
f.write(chr((x >> 16) & 0xff))
|
f.write(chr((x >> 16) & 0xff))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue