Whitespace normalization.

This commit is contained in:
Tim Peters 2001-01-15 00:50:52 +00:00
parent fa25a7d51f
commit 2344fae6d0
17 changed files with 3022 additions and 3024 deletions

View file

@ -1,6 +1,6 @@
"""An object-oriented interface to .netrc files.""" """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
@ -12,7 +12,7 @@ class netrc:
self.hosts = {} self.hosts = {}
self.macros = {} self.macros = {}
lexer = shlex.shlex(fp) lexer = shlex.shlex(fp)
# Allows @ in hostnames. Not a big deal... # Allows @ in hostnames. Not a big deal...
lexer.wordchars = lexer.wordchars + '.-@' lexer.wordchars = lexer.wordchars + '.-@'
while 1: while 1:
# Look for a machine, default, or macdef top-level keyword # Look for a machine, default, or macdef top-level keyword
@ -23,7 +23,7 @@ class netrc:
entryname = lexer.get_token() entryname = lexer.get_token()
elif tt == 'default': elif tt == 'default':
entryname = 'default' entryname = 'default'
elif tt == 'macdef': # Just skip to end of macdefs elif tt == 'macdef': # Just skip to end of macdefs
entryname = lexer.get_token() entryname = lexer.get_token()
self.macros[entryname] = [] self.macros[entryname] = []
lexer.whitepace = ' \t' lexer.whitepace = ' \t'
@ -36,7 +36,7 @@ class netrc:
self.macros[entryname].append(line) self.macros[entryname].append(line)
else: else:
raise SyntaxError, "bad toplevel token %s, file %s, line %d" \ raise SyntaxError, "bad toplevel token %s, file %s, line %d" \
% (tt, file, lexer.lineno) % (tt, file, lexer.lineno)
# We're looking at start of an entry for a named machine or default. # We're looking at start of an entry for a named machine or default.
if toplevel == 'machine': if toplevel == 'machine':
@ -87,6 +87,5 @@ class netrc:
rep = rep + "\n" rep = rep + "\n"
return rep return rep
if __name__ == '__main__': if __name__ == '__main__':
print netrc() print netrc()

View file

@ -34,36 +34,36 @@ import socket
import string import string
# Exceptions raised when an error or invalid response is received # Exceptions raised when an error or invalid response is received
class NNTPError(Exception): class NNTPError(Exception):
"""Base class for all nntplib exceptions""" """Base class for all nntplib exceptions"""
def __init__(self, *args): def __init__(self, *args):
apply(Exception.__init__, (self,)+args) apply(Exception.__init__, (self,)+args)
try: try:
self.response = args[0] self.response = args[0]
except IndexError: except IndexError:
self.response = 'No response given' self.response = 'No response given'
class NNTPReplyError(NNTPError): class NNTPReplyError(NNTPError):
"""Unexpected [123]xx reply""" """Unexpected [123]xx reply"""
pass pass
class NNTPTemporaryError(NNTPError): class NNTPTemporaryError(NNTPError):
"""4xx errors""" """4xx errors"""
pass pass
class NNTPPermanentError(NNTPError): class NNTPPermanentError(NNTPError):
"""5xx errors""" """5xx errors"""
pass pass
class NNTPProtocolError(NNTPError): class NNTPProtocolError(NNTPError):
"""Response does not begin with [1-5]""" """Response does not begin with [1-5]"""
pass pass
class NNTPDataError(NNTPError): class NNTPDataError(NNTPError):
"""Error in response data""" """Error in response data"""
pass pass
# for backwards compatibility # for backwards compatibility
error_reply = NNTPReplyError error_reply = NNTPReplyError
@ -73,7 +73,7 @@ error_proto = NNTPProtocolError
error_data = NNTPDataError error_data = NNTPDataError
# Standard port used by NNTP servers # Standard port used by NNTP servers
NNTP_PORT = 119 NNTP_PORT = 119
@ -86,450 +86,450 @@ LONGRESP = ['100', '215', '220', '221', '222', '224', '230', '231', '282']
CRLF = '\r\n' CRLF = '\r\n'
# The class itself # The class itself
class NNTP: class NNTP:
def __init__(self, host, port=NNTP_PORT, user=None, password=None, def __init__(self, host, port=NNTP_PORT, user=None, password=None,
readermode=None): readermode=None):
"""Initialize an instance. Arguments: """Initialize an instance. Arguments:
- host: hostname to connect to - host: hostname to connect to
- port: port to connect to (default the standard NNTP port) - port: port to connect to (default the standard NNTP port)
- user: username to authenticate with - user: username to authenticate with
- password: password to use with username - password: password to use with username
- readermode: if true, send 'mode reader' command after - readermode: if true, send 'mode reader' command after
connecting. connecting.
readermode is sometimes necessary if you are connecting to an readermode is sometimes necessary if you are connecting to an
NNTP server on the local machine and intend to call NNTP server on the local machine and intend to call
reader-specific comamnds, such as `group'. If you get reader-specific comamnds, such as `group'. If you get
unexpected NNTPPermanentErrors, you might need to set unexpected NNTPPermanentErrors, you might need to set
readermode. readermode.
""" """
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)
self.sock.connect((self.host, self.port)) self.sock.connect((self.host, self.port))
self.file = self.sock.makefile('rb') self.file = self.sock.makefile('rb')
self.debugging = 0 self.debugging = 0
self.welcome = self.getresp() self.welcome = self.getresp()
if readermode: if readermode:
try: try:
self.welcome = self.shortcmd('mode reader') self.welcome = self.shortcmd('mode reader')
except NNTPPermanentError: except NNTPPermanentError:
# error 500, probably 'not implemented' # error 500, probably 'not implemented'
pass pass
if user: if user:
resp = self.shortcmd('authinfo user '+user) resp = self.shortcmd('authinfo user '+user)
if resp[:3] == '381': if resp[:3] == '381':
if not password: if not password:
raise NNTPReplyError(resp) raise NNTPReplyError(resp)
else: else:
resp = self.shortcmd( resp = self.shortcmd(
'authinfo pass '+password) 'authinfo pass '+password)
if resp[:3] != '281': if resp[:3] != '281':
raise NNTPPermanentError(resp) raise NNTPPermanentError(resp)
# Get the welcome message from the server # Get the welcome message from the server
# (this is read and squirreled away by __init__()). # (this is read and squirreled away by __init__()).
# If the response code is 200, posting is allowed; # If the response code is 200, posting is allowed;
# if it 201, posting is not allowed # if it 201, posting is not allowed
def getwelcome(self): def getwelcome(self):
"""Get the welcome message from the server """Get the welcome message from the server
(this is read and squirreled away by __init__()). (this is read and squirreled away by __init__()).
If the response code is 200, posting is allowed; If the response code is 200, posting is allowed;
if it 201, posting is not 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
def set_debuglevel(self, level): def set_debuglevel(self, level):
"""Set the debugging level. Argument 'level' means: """Set the debugging level. Argument 'level' means:
0: no debugging output (default) 0: no debugging output (default)
1: print commands and responses but not body text etc. 1: print commands and responses but not body text etc.
2: also print raw lines read and sent before stripping CR/LF""" 2: also print raw lines read and sent before stripping CR/LF"""
self.debugging = level self.debugging = level
debug = set_debuglevel debug = set_debuglevel
def putline(self, line): def putline(self, line):
"""Internal: send one line to the server, appending CRLF.""" """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)
def putcmd(self, line): def putcmd(self, line):
"""Internal: send one command to the server (through putline()).""" """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)
def getline(self): def getline(self):
"""Internal: return one line from the server, stripping CRLF. """Internal: return one line from the server, stripping CRLF.
Raise EOFError if the connection is closed.""" 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`
if not line: raise EOFError if not line: raise EOFError
if line[-2:] == CRLF: line = line[:-2] if line[-2:] == CRLF: line = line[:-2]
elif line[-1:] in CRLF: line = line[:-1] elif line[-1:] in CRLF: line = line[:-1]
return line return line
def getresp(self): def getresp(self):
"""Internal: get a response from the server. """Internal: get a response from the server.
Raise various errors if the response indicates an error.""" 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]
if c == '4': if c == '4':
raise NNTPTemporaryError(resp) raise NNTPTemporaryError(resp)
if c == '5': if c == '5':
raise NNTPPermanentError(resp) raise NNTPPermanentError(resp)
if c not in '123': if c not in '123':
raise NNTPProtocolError(resp) raise NNTPProtocolError(resp)
return resp return resp
def getlongresp(self): def getlongresp(self):
"""Internal: get a response plus following text from the server. """Internal: get a response plus following text from the server.
Raise various errors if the response indicates an error.""" 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 NNTPReplyError(resp) raise NNTPReplyError(resp)
list = [] list = []
while 1: while 1:
line = self.getline() line = self.getline()
if line == '.': if line == '.':
break break
if line[:2] == '..': if line[:2] == '..':
line = line[1:] line = line[1:]
list.append(line) list.append(line)
return resp, list return resp, list
def shortcmd(self, line): def shortcmd(self, line):
"""Internal: send a command and get the response.""" """Internal: send a command and get the response."""
self.putcmd(line) self.putcmd(line)
return self.getresp() return self.getresp()
def longcmd(self, line): def longcmd(self, line):
"""Internal: send a command and get the response plus following text.""" """Internal: send a command and get the response plus following text."""
self.putcmd(line) self.putcmd(line)
return self.getlongresp() return self.getlongresp()
def newgroups(self, date, time): def newgroups(self, date, time):
"""Process a NEWGROUPS command. Arguments: """Process a NEWGROUPS command. Arguments:
- date: string 'yymmdd' indicating the date - date: string 'yymmdd' indicating the date
- time: string 'hhmmss' indicating the time - time: string 'hhmmss' indicating the time
Return: Return:
- resp: server response if successful - resp: server response if successful
- list: list of newsgroup names""" - list: list of newsgroup names"""
return self.longcmd('NEWGROUPS ' + date + ' ' + time) return self.longcmd('NEWGROUPS ' + date + ' ' + time)
def newnews(self, group, date, time): def newnews(self, group, date, time):
"""Process a NEWNEWS command. Arguments: """Process a NEWNEWS command. Arguments:
- group: group name or '*' - group: group name or '*'
- date: string 'yymmdd' indicating the date - date: string 'yymmdd' indicating the date
- time: string 'hhmmss' indicating the time - time: string 'hhmmss' indicating the time
Return: Return:
- resp: server response if successful - resp: server response if successful
- list: list of article ids""" - list: list of article ids"""
cmd = 'NEWNEWS ' + group + ' ' + date + ' ' + time cmd = 'NEWNEWS ' + group + ' ' + date + ' ' + time
return self.longcmd(cmd) return self.longcmd(cmd)
def list(self): def list(self):
"""Process a LIST command. Return: """Process a LIST command. Return:
- resp: server response if successful - resp: server response if successful
- list: list of (group, last, first, flag) (strings)""" - 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
def group(self, name): def group(self, name):
"""Process a GROUP command. Argument: """Process a GROUP command. Argument:
- group: the group name - group: the group name
Returns: Returns:
- resp: server response if successful - resp: server response if successful
- count: number of articles (string) - count: number of articles (string)
- first: first article number (string) - first: first article number (string)
- last: last article number (string) - last: last article number (string)
- name: the group name""" - name: the group name"""
resp = self.shortcmd('GROUP ' + name) resp = self.shortcmd('GROUP ' + name)
if resp[:3] != '211': if resp[:3] != '211':
raise NNTPReplyError(resp) raise NNTPReplyError(resp)
words = string.split(resp) words = string.split(resp)
count = first = last = 0 count = first = last = 0
n = len(words) n = len(words)
if n > 1: if n > 1:
count = words[1] count = words[1]
if n > 2: if n > 2:
first = words[2] first = words[2]
if n > 3: if n > 3:
last = words[3] last = words[3]
if n > 4: if n > 4:
name = string.lower(words[4]) name = string.lower(words[4])
return resp, count, first, last, name return resp, count, first, last, name
def help(self): def help(self):
"""Process a HELP command. Returns: """Process a HELP command. Returns:
- resp: server response if successful - resp: server response if successful
- list: list of strings""" - list: list of strings"""
return self.longcmd('HELP') return self.longcmd('HELP')
def statparse(self, resp): def statparse(self, resp):
"""Internal: parse the response of a STAT, NEXT or LAST command.""" """Internal: parse the response of a STAT, NEXT or LAST command."""
if resp[:2] != '22': if resp[:2] != '22':
raise NNTPReplyError(resp) raise NNTPReplyError(resp)
words = string.split(resp) words = string.split(resp)
nr = 0 nr = 0
id = '' id = ''
n = len(words) n = len(words)
if n > 1: if n > 1:
nr = words[1] nr = words[1]
if n > 2: if n > 2:
id = words[2] id = words[2]
return resp, nr, id return resp, nr, id
def statcmd(self, line): def statcmd(self, line):
"""Internal: process a STAT, NEXT or LAST command.""" """Internal: process a STAT, NEXT or LAST command."""
resp = self.shortcmd(line) resp = self.shortcmd(line)
return self.statparse(resp) return self.statparse(resp)
def stat(self, id): def stat(self, id):
"""Process a STAT command. Argument: """Process a STAT command. Argument:
- id: article number or message id - id: article number or message id
Returns: Returns:
- resp: server response if successful - resp: server response if successful
- nr: the article number - nr: the article number
- id: the article id""" - id: the article id"""
return self.statcmd('STAT ' + id) return self.statcmd('STAT ' + id)
def next(self): def next(self):
"""Process a NEXT command. No arguments. Return as for STAT.""" """Process a NEXT command. No arguments. Return as for STAT."""
return self.statcmd('NEXT') return self.statcmd('NEXT')
def last(self): def last(self):
"""Process a LAST command. No arguments. Return as for STAT.""" """Process a LAST command. No arguments. Return as for STAT."""
return self.statcmd('LAST') return self.statcmd('LAST')
def artcmd(self, line): def artcmd(self, line):
"""Internal: process a HEAD, BODY or ARTICLE command.""" """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
def head(self, id): def head(self, id):
"""Process a HEAD command. Argument: """Process a HEAD command. Argument:
- id: article number or message id - id: article number or message id
Returns: Returns:
- resp: server response if successful - resp: server response if successful
- nr: article number - nr: article number
- id: message id - id: message id
- list: the lines of the article's header""" - list: the lines of the article's header"""
return self.artcmd('HEAD ' + id) return self.artcmd('HEAD ' + id)
def body(self, id): def body(self, id):
"""Process a BODY command. Argument: """Process a BODY command. Argument:
- id: article number or message id - id: article number or message id
Returns: Returns:
- resp: server response if successful - resp: server response if successful
- nr: article number - nr: article number
- id: message id - id: message id
- list: the lines of the article's body""" - list: the lines of the article's body"""
return self.artcmd('BODY ' + id) return self.artcmd('BODY ' + id)
def article(self, id): def article(self, id):
"""Process an ARTICLE command. Argument: """Process an ARTICLE command. Argument:
- id: article number or message id - id: article number or message id
Returns: Returns:
- resp: server response if successful - resp: server response if successful
- nr: article number - nr: article number
- id: message id - id: message id
- list: the lines of the article""" - list: the lines of the article"""
return self.artcmd('ARTICLE ' + id) return self.artcmd('ARTICLE ' + id)
def slave(self): def slave(self):
"""Process a SLAVE command. Returns: """Process a SLAVE command. Returns:
- resp: server response if successful""" - resp: server response if successful"""
return self.shortcmd('SLAVE') return self.shortcmd('SLAVE')
def xhdr(self, hdr, str): def xhdr(self, hdr, str):
"""Process an XHDR command (optional server extension). Arguments: """Process an XHDR command (optional server extension). Arguments:
- hdr: the header type (e.g. 'subject') - hdr: the header type (e.g. 'subject')
- str: an article nr, a message id, or a range nr1-nr2 - str: an article nr, a message id, or a range nr1-nr2
Returns: Returns:
- resp: server response if successful - resp: server response if successful
- list: list of (nr, value) strings""" - 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)):
line = lines[i] line = lines[i]
m = pat.match(line) m = pat.match(line)
if m: if m:
lines[i] = m.group(1, 2) lines[i] = m.group(1, 2)
return resp, lines return resp, lines
def xover(self,start,end): def xover(self,start,end):
"""Process an XOVER command (optional server extension) Arguments: """Process an XOVER command (optional server extension) Arguments:
- start: start of range - start: start of range
- end: end of range - end: end of range
Returns: Returns:
- resp: server response if successful - resp: server response if successful
- list: list of (art-nr, subject, poster, date, - list: list of (art-nr, subject, poster, date,
id, references, size, lines)""" 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:
elem = string.splitfields(line,"\t") elem = string.splitfields(line,"\t")
try: try:
xover_lines.append((elem[0], xover_lines.append((elem[0],
elem[1], elem[1],
elem[2], elem[2],
elem[3], elem[3],
elem[4], elem[4],
string.split(elem[5]), string.split(elem[5]),
elem[6], elem[6],
elem[7])) elem[7]))
except IndexError: except IndexError:
raise NNTPDataError(line) raise NNTPDataError(line)
return resp,xover_lines return resp,xover_lines
def xgtitle(self, group): def xgtitle(self, group):
"""Process an XGTITLE command (optional server extension) Arguments: """Process an XGTITLE command (optional server extension) Arguments:
- group: group name wildcard (i.e. news.*) - group: group name wildcard (i.e. news.*)
Returns: Returns:
- resp: server response if successful - resp: server response if successful
- list: list of (name,title) strings""" - 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 = []
for raw_line in raw_lines: for raw_line in raw_lines:
match = line_pat.search(string.strip(raw_line)) match = line_pat.search(string.strip(raw_line))
if match: if match:
lines.append(match.group(1, 2)) lines.append(match.group(1, 2))
return resp, lines return resp, lines
def xpath(self,id): def xpath(self,id):
"""Process an XPATH command (optional server extension) Arguments: """Process an XPATH command (optional server extension) Arguments:
- id: Message id of article - id: Message id of article
Returns: Returns:
resp: server response if successful resp: server response if successful
path: directory path to article""" path: directory path to article"""
resp = self.shortcmd("XPATH " + id) resp = self.shortcmd("XPATH " + id)
if resp[:3] != '223': if resp[:3] != '223':
raise NNTPReplyError(resp) raise NNTPReplyError(resp)
try: try:
[resp_num, path] = string.split(resp) [resp_num, path] = string.split(resp)
except ValueError: except ValueError:
raise NNTPReplyError(resp) raise NNTPReplyError(resp)
else: else:
return resp, path return resp, path
def date (self): def date (self):
"""Process the DATE command. Arguments: """Process the DATE command. Arguments:
None None
Returns: Returns:
resp: server response if successful resp: server response if successful
date: Date suitable for newnews/newgroups commands etc. date: Date suitable for newnews/newgroups commands etc.
time: Time 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 NNTPReplyError(resp) raise NNTPReplyError(resp)
elem = string.split(resp) elem = string.split(resp)
if len(elem) != 2: if len(elem) != 2:
raise NNTPDataError(resp) raise NNTPDataError(resp)
date = elem[1][2:8] date = elem[1][2:8]
time = elem[1][-6:] time = elem[1][-6:]
if len(date) != 6 or len(time) != 6: if len(date) != 6 or len(time) != 6:
raise NNTPDataError(resp) raise NNTPDataError(resp)
return resp, date, time return resp, date, time
def post(self, f): def post(self, f):
"""Process a POST command. Arguments: """Process a POST command. Arguments:
- f: file containing the article - f: file containing the article
Returns: Returns:
- resp: server response if successful""" - resp: server response if successful"""
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':
raise NNTPReplyError(resp) raise NNTPReplyError(resp)
while 1: while 1:
line = f.readline() line = f.readline()
if not line: if not line:
break break
if line[-1] == '\n': if line[-1] == '\n':
line = line[:-1] line = line[:-1]
if line[:1] == '.': if line[:1] == '.':
line = '.' + line line = '.' + line
self.putline(line) self.putline(line)
self.putline('.') self.putline('.')
return self.getresp() return self.getresp()
def ihave(self, id, f): def ihave(self, id, f):
"""Process an IHAVE command. Arguments: """Process an IHAVE command. Arguments:
- id: message-id of the article - id: message-id of the article
- f: file containing the article - f: file containing the article
Returns: Returns:
- resp: server response if successful - resp: server response if successful
Note that if the server refuses the article an exception is raised.""" 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':
raise NNTPReplyError(resp) raise NNTPReplyError(resp)
while 1: while 1:
line = f.readline() line = f.readline()
if not line: if not line:
break break
if line[-1] == '\n': if line[-1] == '\n':
line = line[:-1] line = line[:-1]
if line[:1] == '.': if line[:1] == '.':
line = '.' + line line = '.' + line
self.putline(line) self.putline(line)
self.putline('.') self.putline('.')
return self.getresp() return self.getresp()
def quit(self): def quit(self):
"""Process a QUIT command and close the socket. Returns: """Process a QUIT command and close the socket. Returns:
- resp: server response if successful""" - resp: server response if successful"""
resp = self.shortcmd('QUIT') resp = self.shortcmd('QUIT')
self.file.close() self.file.close()
self.sock.close() self.sock.close()
del self.file, self.sock del self.file, self.sock
return resp return resp
def _test(): def _test():
"""Minimal test function.""" """Minimal test function."""
s = NNTP('news', readermode='reader') s = NNTP('news', readermode='reader')
resp, count, first, last, name = s.group('comp.lang.python') resp, count, first, last, name = s.group('comp.lang.python')
print resp print resp
print 'Group', name, 'has', count, 'articles, range', first, 'to', last print 'Group', name, 'has', count, 'articles, range', first, 'to', last
resp, subs = s.xhdr('subject', first + '-' + last) resp, subs = s.xhdr('subject', first + '-' + last)
print resp print resp
for item in subs: for item in subs:
print "%7s %s" % item print "%7s %s" % item
resp = s.quit() resp = s.quit()
print resp print resp
# Run the test when run as a script # Run the test when run as a script
if __name__ == '__main__': if __name__ == '__main__':
_test() _test()

View file

@ -1,5 +1,5 @@
# Module 'ntpath' -- common operations on WinNT/Win95 pathnames # Module 'ntpath' -- common operations on WinNT/Win95 pathnames
"""Common pathname manipulations, WindowsNT/95 version. """Common pathname manipulations, WindowsNT/95 version.
Instead of importing this module directly, import os and refer to this Instead of importing this module directly, import os and refer to this
module as os.path. module as os.path.
@ -254,7 +254,7 @@ def ismount(path):
def walk(top, func, arg): def walk(top, func, arg):
"""Directory tree walk whth callback function. """Directory tree walk whth callback function.
walk(top, func, arg) calls func(arg, d, files) for each directory d walk(top, func, arg) calls func(arg, d, files) for each directory d
in the tree rooted at top (including top itself); files is a list in the tree rooted at top (including top itself); files is a list
of all the files and subdirs in directory d.""" of all the files and subdirs in directory d."""
try: try:
@ -313,7 +313,7 @@ def expanduser(path):
# XXX With COMMAND.COM you can use any characters in a variable name, # XXX With COMMAND.COM you can use any characters in a variable name,
# XXX except '^|<>='. # XXX except '^|<>='.
def expandvars(path): def expandvars(path):
"""Expand shell variables of form $var and ${var}. """Expand shell variables of form $var and ${var}.
Unknown variables are left unchanged.""" Unknown variables are left unchanged."""

View file

@ -1,66 +1,66 @@
"""Convert a NT pathname to a file URL and vice versa.""" """Convert a NT pathname to a file URL and vice versa."""
def url2pathname(url): def url2pathname(url):
r"""Convert a URL to a DOS path. r"""Convert a URL to a DOS path.
///C|/foo/bar/spam.foo ///C|/foo/bar/spam.foo
becomes becomes
C:\foo\bar\spam.foo C:\foo\bar\spam.foo
""" """
import string, urllib import string, urllib
if not '|' in url: if not '|' in url:
# No drive specifier, just convert slashes # No drive specifier, just convert slashes
if url[:4] == '////': if url[:4] == '////':
# path is something like ////host/path/on/remote/host # path is something like ////host/path/on/remote/host
# convert this to \\host\path\on\remote\host # convert this to \\host\path\on\remote\host
# (notice halving of slashes at the start of the path) # (notice halving of slashes at the start of the path)
url = url[2:] url = url[2:]
components = string.split(url, '/') components = string.split(url, '/')
# make sure not to convert quoted slashes :-) # make sure not to convert quoted slashes :-)
return urllib.unquote(string.join(components, '\\')) return urllib.unquote(string.join(components, '\\'))
comp = string.split(url, '|') comp = string.split(url, '|')
if len(comp) != 2 or comp[0][-1] not in string.letters: if len(comp) != 2 or comp[0][-1] not in string.letters:
error = 'Bad URL: ' + url error = 'Bad URL: ' + url
raise IOError, error raise IOError, error
drive = string.upper(comp[0][-1]) drive = string.upper(comp[0][-1])
components = string.split(comp[1], '/') components = string.split(comp[1], '/')
path = drive + ':' path = drive + ':'
for comp in components: for comp in components:
if comp: if comp:
path = path + '\\' + urllib.unquote(comp) path = path + '\\' + urllib.unquote(comp)
return path return path
def pathname2url(p): def pathname2url(p):
r"""Convert a DOS path name to a file url. r"""Convert a DOS path name to a file url.
C:\foo\bar\spam.foo C:\foo\bar\spam.foo
becomes becomes
///C|/foo/bar/spam.foo ///C|/foo/bar/spam.foo
""" """
import string, urllib import string, urllib
if not ':' in p: if not ':' in p:
# No drive specifier, just convert slashes and quote the name # No drive specifier, just convert slashes and quote the name
if p[:2] == '\\\\': if p[:2] == '\\\\':
# path is something like \\host\path\on\remote\host # path is something like \\host\path\on\remote\host
# convert this to ////host/path/on/remote/host # convert this to ////host/path/on/remote/host
# (notice doubling of slashes at the start of the path) # (notice doubling of slashes at the start of the path)
p = '\\\\' + p p = '\\\\' + p
components = string.split(p, '\\') components = string.split(p, '\\')
return urllib.quote(string.join(components, '/')) return urllib.quote(string.join(components, '/'))
comp = string.split(p, ':') comp = string.split(p, ':')
if len(comp) != 2 or len(comp[0]) > 1: if len(comp) != 2 or len(comp[0]) > 1:
error = 'Bad path: ' + p error = 'Bad path: ' + p
raise IOError, error raise IOError, error
drive = urllib.quote(string.upper(comp[0])) drive = urllib.quote(string.upper(comp[0]))
components = string.split(comp[1], '\\') components = string.split(comp[1], '\\')
path = '///' + drive + '|' path = '///' + drive + '|'
for comp in components: for comp in components:
if comp: if comp:
path = path + '/' + urllib.quote(comp) path = path + '/' + urllib.quote(comp)
return path return path

View file

@ -213,7 +213,7 @@ def execlpe(file, *args):
Execute the executable file (which is searched for along $PATH) Execute the executable file (which is searched for along $PATH)
with argument list args and environment env, replacing the current with argument list args and environment env, replacing the current
process. """ process. """
env = args[-1] env = args[-1]
execvpe(file, args[:-1], env) execvpe(file, args[:-1], env)
@ -231,7 +231,7 @@ def execvpe(file, args, env):
Execute the executable file (which is searched for along $PATH) Execute the executable file (which is searched for along $PATH)
with argument list args and environment env , replacing the with argument list args and environment env , replacing the
current process. current process.
args may be a list or tuple of strings. """ args may be a list or tuple of strings. """
_execvpe(file, args, env) _execvpe(file, args, env)
_notfound = None _notfound = None
@ -370,7 +370,7 @@ if _exists("fork") and not _exists("spawnv") and _exists("execv"):
Execute file with arguments from args in a subprocess. Execute file with arguments from args in a subprocess.
If mode == P_NOWAIT return the pid of the process. If mode == P_NOWAIT return the pid of the process.
If mode == P_WAIT return the process's exit code if it exits normally; If mode == P_WAIT return the process's exit code if it exits normally;
otherwise return -SIG, where SIG is the signal that killed it. """ otherwise return -SIG, where SIG is the signal that killed it. """
return _spawnvef(mode, file, args, None, execv) return _spawnvef(mode, file, args, None, execv)
def spawnve(mode, file, args, env): def spawnve(mode, file, args, env):

1766
Lib/pdb.py

File diff suppressed because it is too large Load diff

View file

@ -123,7 +123,7 @@ class Pickler:
return LONG_BINGET + s return LONG_BINGET + s
return GET + `i` + '\n' return GET + `i` + '\n'
def save(self, object, pers_save = 0): def save(self, object, pers_save = 0):
memo = self.memo memo = self.memo
@ -134,7 +134,7 @@ class Pickler:
return return
d = id(object) d = id(object)
t = type(object) t = type(object)
if ((t is TupleType) and (len(object) == 0)): if ((t is TupleType) and (len(object) == 0)):
@ -179,14 +179,14 @@ class Pickler:
"tuple" % reduce "tuple" % reduce
l = len(tup) l = len(tup)
if ((l != 2) and (l != 3)): if ((l != 2) and (l != 3)):
raise PicklingError, "tuple returned by %s must contain " \ raise PicklingError, "tuple returned by %s must contain " \
"only two or three elements" % reduce "only two or three elements" % reduce
callable = tup[0] callable = tup[0]
arg_tup = tup[1] arg_tup = tup[1]
if (l > 2): if (l > 2):
state = tup[2] state = tup[2]
else: else:
@ -196,7 +196,7 @@ class Pickler:
raise PicklingError, "Second element of tuple returned " \ raise PicklingError, "Second element of tuple returned " \
"by %s must be a tuple" % reduce "by %s must be a tuple" % reduce
self.save_reduce(callable, arg_tup, state) self.save_reduce(callable, arg_tup, state)
memo_len = len(memo) memo_len = len(memo)
self.write(self.put(memo_len)) self.write(self.put(memo_len))
memo[d] = (memo_len, object) memo[d] = (memo_len, object)
@ -224,7 +224,7 @@ class Pickler:
save(callable) save(callable)
save(arg_tup) save(arg_tup)
write(REDUCE) write(REDUCE)
if (state is not None): if (state is not None):
save(state) save(state)
write(BUILD) write(BUILD)
@ -317,7 +317,7 @@ class Pickler:
if (self.bin): if (self.bin):
write(POP_MARK + self.get(memo[d][0])) write(POP_MARK + self.get(memo[d][0]))
return return
write(POP * (len(object) + 1) + self.get(memo[d][0])) write(POP * (len(object) + 1) + self.get(memo[d][0]))
return return
@ -352,7 +352,7 @@ class Pickler:
for element in object: for element in object:
save(element) save(element)
if (not using_appends): if (not using_appends):
write(APPEND) write(APPEND)
@ -542,7 +542,7 @@ class Unpickler:
def load_binpersid(self): def load_binpersid(self):
stack = self.stack stack = self.stack
pid = stack[-1] pid = stack[-1]
del stack[-1] del stack[-1]
@ -568,7 +568,7 @@ class Unpickler:
def load_binint2(self): def load_binint2(self):
self.append(mloads('i' + self.read(2) + '\000\000')) self.append(mloads('i' + self.read(2) + '\000\000'))
dispatch[BININT2] = load_binint2 dispatch[BININT2] = load_binint2
def load_long(self): def load_long(self):
self.append(long(self.readline()[:-1], 0)) self.append(long(self.readline()[:-1], 0))
dispatch[LONG] = load_long dispatch[LONG] = load_long
@ -710,7 +710,7 @@ class Unpickler:
k = self.marker() k = self.marker()
klass = stack[k + 1] klass = stack[k + 1]
del stack[k + 1] del stack[k + 1]
args = tuple(stack[k + 1:]) args = tuple(stack[k + 1:])
del stack[k:] del stack[k:]
instantiated = 0 instantiated = 0
if (not args and type(klass) is ClassType and if (not args and type(klass) is ClassType and
@ -726,7 +726,7 @@ class Unpickler:
if not instantiated: if not instantiated:
value = apply(klass, args) value = apply(klass, args)
self.append(value) self.append(value)
dispatch[OBJ] = load_obj dispatch[OBJ] = load_obj
def load_global(self): def load_global(self):
module = self.readline()[:-1] module = self.readline()[:-1]
@ -761,8 +761,8 @@ class Unpickler:
safe = None safe = None
if (not safe): if (not safe):
raise UnpicklingError, "%s is not safe for " \ raise UnpicklingError, "%s is not safe for " \
"unpickling" % callable "unpickling" % callable
if arg_tup is None: if arg_tup is None:
value = callable.__basicnew__() value = callable.__basicnew__()
@ -829,7 +829,7 @@ class Unpickler:
del stack[mark:] del stack[mark:]
dispatch[APPENDS] = load_appends dispatch[APPENDS] = load_appends
def load_setitem(self): def load_setitem(self):
stack = self.stack stack = self.stack
value = stack[-1] value = stack[-1]

View file

@ -69,229 +69,229 @@ import string
# Conversion step kinds # Conversion step kinds
FILEIN_FILEOUT = 'ff' # Must read & write real files FILEIN_FILEOUT = 'ff' # Must read & write real files
STDIN_FILEOUT = '-f' # Must write a real file STDIN_FILEOUT = '-f' # Must write a real file
FILEIN_STDOUT = 'f-' # Must read a real file FILEIN_STDOUT = 'f-' # Must read a real file
STDIN_STDOUT = '--' # Normal pipeline element STDIN_STDOUT = '--' # Normal pipeline element
SOURCE = '.-' # Must be first, writes stdout SOURCE = '.-' # Must be first, writes stdout
SINK = '-.' # Must be last, reads stdin SINK = '-.' # Must be last, reads stdin
stepkinds = [FILEIN_FILEOUT, STDIN_FILEOUT, FILEIN_STDOUT, STDIN_STDOUT, \ stepkinds = [FILEIN_FILEOUT, STDIN_FILEOUT, FILEIN_STDOUT, STDIN_STDOUT, \
SOURCE, SINK] SOURCE, SINK]
class Template: class Template:
"""Class representing a pipeline template.""" """Class representing a pipeline template."""
def __init__(self): def __init__(self):
"""Template() returns a fresh pipeline template.""" """Template() returns a fresh pipeline template."""
self.debugging = 0 self.debugging = 0
self.reset() self.reset()
def __repr__(self): def __repr__(self):
"""t.__repr__() implements `t`.""" """t.__repr__() implements `t`."""
return '<Template instance, steps=' + `self.steps` + '>' return '<Template instance, steps=' + `self.steps` + '>'
def reset(self): def reset(self):
"""t.reset() restores a pipeline template to its initial state.""" """t.reset() restores a pipeline template to its initial state."""
self.steps = [] self.steps = []
def clone(self): def clone(self):
"""t.clone() returns a new pipeline template with identical """t.clone() returns a new pipeline template with identical
initial state as the current one.""" 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
def debug(self, flag): def debug(self, flag):
"""t.debug(flag) turns debugging on or off.""" """t.debug(flag) turns debugging on or off."""
self.debugging = flag self.debugging = flag
def append(self, cmd, kind): def append(self, cmd, kind):
"""t.append(cmd, kind) adds a new step at the end.""" """t.append(cmd, kind) adds a new step at the end."""
if type(cmd) is not type(''): if type(cmd) is not type(''):
raise TypeError, \ raise TypeError, \
'Template.append: cmd must be a string' 'Template.append: cmd must be a string'
if kind not in stepkinds: if kind not in stepkinds:
raise ValueError, \ raise ValueError, \
'Template.append: bad kind ' + `kind` 'Template.append: bad kind ' + `kind`
if kind == SOURCE: if kind == SOURCE:
raise ValueError, \ raise ValueError, \
'Template.append: SOURCE can only be prepended' 'Template.append: SOURCE can only be prepended'
if self.steps and self.steps[-1][1] == SINK: if self.steps and self.steps[-1][1] == SINK:
raise ValueError, \ raise ValueError, \
'Template.append: already ends with SINK' 'Template.append: already ends with SINK'
if kind[0] == 'f' and not re.search('\$IN\b', cmd): if kind[0] == 'f' and not re.search('\$IN\b', cmd):
raise ValueError, \ raise ValueError, \
'Template.append: missing $IN in cmd' 'Template.append: missing $IN in cmd'
if kind[1] == 'f' and not re.search('\$OUT\b', cmd): if kind[1] == 'f' and not re.search('\$OUT\b', cmd):
raise ValueError, \ raise ValueError, \
'Template.append: missing $OUT in cmd' 'Template.append: missing $OUT in cmd'
self.steps.append((cmd, kind)) self.steps.append((cmd, kind))
def prepend(self, cmd, kind): def prepend(self, cmd, kind):
"""t.prepend(cmd, kind) adds a new step at the front.""" """t.prepend(cmd, kind) adds a new step at the front."""
if type(cmd) is not type(''): if type(cmd) is not type(''):
raise TypeError, \ raise TypeError, \
'Template.prepend: cmd must be a string' 'Template.prepend: cmd must be a string'
if kind not in stepkinds: if kind not in stepkinds:
raise ValueError, \ raise ValueError, \
'Template.prepend: bad kind ' + `kind` 'Template.prepend: bad kind ' + `kind`
if kind == SINK: if kind == SINK:
raise ValueError, \ raise ValueError, \
'Template.prepend: SINK can only be appended' 'Template.prepend: SINK can only be appended'
if self.steps and self.steps[0][1] == SOURCE: if self.steps and self.steps[0][1] == SOURCE:
raise ValueError, \ raise ValueError, \
'Template.prepend: already begins with SOURCE' 'Template.prepend: already begins with SOURCE'
if kind[0] == 'f' and not re.search('\$IN\b', cmd): if kind[0] == 'f' and not re.search('\$IN\b', cmd):
raise ValueError, \ raise ValueError, \
'Template.prepend: missing $IN in cmd' 'Template.prepend: missing $IN in cmd'
if kind[1] == 'f' and not re.search('\$OUT\b', cmd): if kind[1] == 'f' and not re.search('\$OUT\b', cmd):
raise ValueError, \ raise ValueError, \
'Template.prepend: missing $OUT in cmd' 'Template.prepend: missing $OUT in cmd'
self.steps.insert(0, (cmd, kind)) self.steps.insert(0, (cmd, kind))
def open(self, file, rw): def open(self, file, rw):
"""t.open(file, rw) returns a pipe or file object open for """t.open(file, rw) returns a pipe or file object open for
reading or writing; the file is the other end of the pipeline.""" 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':
return self.open_w(file) return self.open_w(file)
raise ValueError, \ raise ValueError, \
'Template.open: rw must be \'r\' or \'w\', not ' + `rw` 'Template.open: rw must be \'r\' or \'w\', not ' + `rw`
def open_r(self, file): def open_r(self, file):
"""t.open_r(file) and t.open_w(file) implement """t.open_r(file) and t.open_w(file) implement
t.open(file, 'r') and t.open(file, 'w') respectively.""" t.open(file, 'r') and t.open(file, 'w') respectively."""
if not self.steps: if not self.steps:
return open(file, 'r') return open(file, 'r')
if self.steps[-1][1] == SINK: if self.steps[-1][1] == SINK:
raise ValueError, \ raise ValueError, \
'Template.open_r: pipeline ends width SINK' 'Template.open_r: pipeline ends width SINK'
cmd = self.makepipeline(file, '') cmd = self.makepipeline(file, '')
return os.popen(cmd, 'r') return os.popen(cmd, 'r')
def open_w(self, file): def open_w(self, file):
if not self.steps: if not self.steps:
return open(file, 'w') return open(file, 'w')
if self.steps[0][1] == SOURCE: if self.steps[0][1] == SOURCE:
raise ValueError, \ raise ValueError, \
'Template.open_w: pipeline begins with SOURCE' 'Template.open_w: pipeline begins with SOURCE'
cmd = self.makepipeline('', file) cmd = self.makepipeline('', file)
return os.popen(cmd, 'w') return os.popen(cmd, 'w')
def copy(self, infile, outfile): def copy(self, infile, outfile):
return os.system(self.makepipeline(infile, outfile)) return os.system(self.makepipeline(infile, outfile))
def makepipeline(self, infile, outfile): def makepipeline(self, infile, outfile):
cmd = makepipeline(infile, self.steps, outfile) cmd = makepipeline(infile, self.steps, outfile)
if self.debugging: if self.debugging:
print cmd print cmd
cmd = 'set -x; ' + cmd cmd = 'set -x; ' + cmd
return cmd return cmd
def makepipeline(infile, steps, outfile): def makepipeline(infile, steps, outfile):
# Build a list with for each command: # Build a list with for each command:
# [input filename or '', command string, kind, output filename or ''] # [input filename or '', command string, kind, output filename or '']
list = [] list = []
for cmd, kind in steps: for cmd, kind in steps:
list.append(['', cmd, kind, '']) list.append(['', cmd, kind, ''])
# #
# Make sure there is at least one step # Make sure there is at least one step
# #
if not list: if not list:
list.append(['', 'cat', '--', '']) list.append(['', 'cat', '--', ''])
# #
# Take care of the input and output ends # Take care of the input and output ends
# #
[cmd, kind] = list[0][1:3] [cmd, kind] = list[0][1:3]
if kind[0] == 'f' and not infile: if kind[0] == 'f' and not infile:
list.insert(0, ['', 'cat', '--', '']) list.insert(0, ['', 'cat', '--', ''])
list[0][0] = infile list[0][0] = infile
# #
[cmd, kind] = list[-1][1:3] [cmd, kind] = list[-1][1:3]
if kind[1] == 'f' and not outfile: if kind[1] == 'f' and not outfile:
list.append(['', 'cat', '--', '']) list.append(['', 'cat', '--', ''])
list[-1][-1] = outfile list[-1][-1] = outfile
# #
# Invent temporary files to connect stages that need files # Invent temporary files to connect stages that need files
# #
garbage = [] garbage = []
for i in range(1, len(list)): for i in range(1, len(list)):
lkind = list[i-1][2] lkind = list[i-1][2]
rkind = list[i][2] rkind = list[i][2]
if lkind[1] == 'f' or rkind[0] == 'f': if lkind[1] == 'f' or rkind[0] == 'f':
temp = tempfile.mktemp() temp = tempfile.mktemp()
garbage.append(temp) garbage.append(temp)
list[i-1][-1] = list[i][0] = temp list[i-1][-1] = list[i][0] = temp
# #
for item in list: for item in list:
[inf, cmd, kind, outf] = item [inf, cmd, kind, outf] = item
if kind[1] == 'f': if kind[1] == 'f':
cmd = 'OUT=' + quote(outf) + '; ' + cmd cmd = 'OUT=' + quote(outf) + '; ' + cmd
if kind[0] == 'f': if kind[0] == 'f':
cmd = 'IN=' + quote(inf) + '; ' + cmd cmd = 'IN=' + quote(inf) + '; ' + cmd
if kind[0] == '-' and inf: if kind[0] == '-' and inf:
cmd = cmd + ' <' + quote(inf) cmd = cmd + ' <' + quote(inf)
if kind[1] == '-' and outf: if kind[1] == '-' and outf:
cmd = cmd + ' >' + quote(outf) cmd = cmd + ' >' + quote(outf)
item[1] = cmd item[1] = cmd
# #
cmdlist = list[0][1] cmdlist = list[0][1]
for item in list[1:]: for item in list[1:]:
[cmd, kind] = item[1:3] [cmd, kind] = item[1:3]
if item[0] == '': if item[0] == '':
if 'f' in kind: if 'f' in kind:
cmd = '{ ' + cmd + '; }' cmd = '{ ' + cmd + '; }'
cmdlist = cmdlist + ' |\n' + cmd cmdlist = cmdlist + ' |\n' + cmd
else: else:
cmdlist = cmdlist + '\n' + cmd cmdlist = cmdlist + '\n' + cmd
# #
if garbage: if garbage:
rmcmd = 'rm -f' rmcmd = 'rm -f'
for file in garbage: for file in garbage:
rmcmd = rmcmd + ' ' + quote(file) rmcmd = rmcmd + ' ' + quote(file)
trapcmd = 'trap ' + quote(rmcmd + '; exit') + ' 1 2 3 13 14 15' trapcmd = 'trap ' + quote(rmcmd + '; exit') + ' 1 2 3 13 14 15'
cmdlist = trapcmd + '\n' + cmdlist + '\n' + rmcmd cmdlist = trapcmd + '\n' + cmdlist + '\n' + rmcmd
# #
return cmdlist return cmdlist
# Reliably quote a string as a single argument for /bin/sh # Reliably quote a string as a single argument for /bin/sh
_safechars = string.letters + string.digits + '!@%_-+=:,./' # Safe unquoted _safechars = string.letters + string.digits + '!@%_-+=:,./' # Safe unquoted
_funnychars = '"`$\\' # Unsafe inside "double quotes" _funnychars = '"`$\\' # Unsafe inside "double quotes"
def quote(file): def quote(file):
for c in file: for c in file:
if c not in _safechars: if c not in _safechars:
break break
else: else:
return file return file
if '\'' not in file: if '\'' not in file:
return '\'' + file + '\'' return '\'' + file + '\''
res = '' res = ''
for c in file: for c in file:
if c in _funnychars: if c in _funnychars:
c = '\\' + c c = '\\' + c
res = res + c res = res + c
return '"' + res + '"' return '"' + res + '"'
# Small test program and example # Small test program and example
def test(): def test():
print 'Testing...' print 'Testing...'
t = Template() t = Template()
t.append('togif $IN $OUT', 'ff') t.append('togif $IN $OUT', 'ff')
t.append('giftoppm', '--') t.append('giftoppm', '--')
t.append('ppmtogif >$OUT', '-f') t.append('ppmtogif >$OUT', '-f')
t.append('fromgif $IN $OUT', 'ff') t.append('fromgif $IN $OUT', 'ff')
t.debug(1) t.debug(1)
FILE = '/usr/local/images/rgb/rogues/guido.rgb' FILE = '/usr/local/images/rgb/rogues/guido.rgb'
t.copy(FILE, '@temp') t.copy(FILE, '@temp')
print 'Done.' print 'Done.'

View file

@ -32,290 +32,290 @@ CRLF = CR+LF
class POP3: class POP3:
"""This class supports both the minimal and optional command sets. """This class supports both the minimal and optional command sets.
Arguments can be strings or integers (where appropriate) Arguments can be strings or integers (where appropriate)
(e.g.: retr(1) and retr('1') both work equally well. (e.g.: retr(1) and retr('1') both work equally well.
Minimal Command Set: Minimal Command Set:
USER name user(name) USER name user(name)
PASS string pass_(string) PASS string pass_(string)
STAT stat() STAT stat()
LIST [msg] list(msg = None) LIST [msg] list(msg = None)
RETR msg retr(msg) RETR msg retr(msg)
DELE msg dele(msg) DELE msg dele(msg)
NOOP noop() NOOP noop()
RSET rset() RSET rset()
QUIT quit() QUIT quit()
Optional Commands (some servers support these): Optional Commands (some servers support these):
RPOP name rpop(name) RPOP name rpop(name)
APOP name digest apop(name, digest) APOP name digest apop(name, digest)
TOP msg n top(msg, n) TOP msg n top(msg, n)
UIDL [msg] uidl(msg = None) UIDL [msg] uidl(msg = None)
Raises one exception: 'error_proto'. Raises one exception: 'error_proto'.
Instantiate with: Instantiate with:
POP3(hostname, port=110) POP3(hostname, port=110)
NB: the POP protocol locks the mailbox from user NB: the POP protocol locks the mailbox from user
authorization until QUIT, so be sure to get in, suck authorization until QUIT, so be sure to get in, suck
the messages, and quit, each time you access the the messages, and quit, each time you access the
mailbox. mailbox.
POP is a line-based protocol, which means large mail POP is a line-based protocol, which means large mail
messages consume lots of python cycles reading them messages consume lots of python cycles reading them
line-by-line. line-by-line.
If it's available on your mail server, use IMAP4 If it's available on your mail server, use IMAP4
instead, it doesn't suffer from the two problems instead, it doesn't suffer from the two problems
above. above.
""" """
def __init__(self, host, port = POP3_PORT): def __init__(self, host, port = POP3_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)
self.sock.connect((self.host, self.port)) self.sock.connect((self.host, self.port))
self.file = self.sock.makefile('rb') self.file = self.sock.makefile('rb')
self._debugging = 0 self._debugging = 0
self.welcome = self._getresp() self.welcome = self._getresp()
def _putline(self, line): def _putline(self, line):
#if self._debugging > 1: print '*put*', `line` #if self._debugging > 1: print '*put*', `line`
self.sock.send('%s%s' % (line, CRLF)) self.sock.send('%s%s' % (line, CRLF))
# Internal: send one command to the server (through _putline()) # Internal: send one command to the server (through _putline())
def _putcmd(self, line): def _putcmd(self, line):
#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. # Internal: return one line from the server, stripping CRLF.
# This is where all the CPU time of this module is consumed. # This is where all the CPU time of this module is consumed.
# Raise error_proto('-ERR EOF') if the connection is closed. # Raise error_proto('-ERR EOF') if the connection is closed.
def _getline(self): def _getline(self):
line = self.file.readline() line = self.file.readline()
#if self._debugging > 1: print '*get*', `line` #if self._debugging > 1: print '*get*', `line`
if not line: raise error_proto('-ERR EOF') if not line: raise error_proto('-ERR EOF')
octets = len(line) octets = len(line)
# server can send any combination of CR & LF # server can send any combination of CR & LF
# however, 'readline()' returns lines ending in LF # however, 'readline()' returns lines ending in LF
# so only possibilities are ...LF, ...CRLF, CR...LF # so only possibilities are ...LF, ...CRLF, CR...LF
if line[-2:] == CRLF: if line[-2:] == CRLF:
return line[:-2], octets return line[:-2], octets
if line[0] == CR: if line[0] == CR:
return line[1:-1], octets return line[1:-1], octets
return line[:-1], octets return line[:-1], octets
# Internal: get a response from the server. # Internal: get a response from the server.
# Raise 'error_proto' if the response doesn't start with '+'. # Raise 'error_proto' if the response doesn't start with '+'.
def _getresp(self): def _getresp(self):
resp, o = self._getline() resp, o = self._getline()
#if self._debugging > 1: print '*resp*', `resp` #if self._debugging > 1: print '*resp*', `resp`
c = resp[:1] c = resp[:1]
if c != '+': if c != '+':
raise error_proto(resp) raise error_proto(resp)
return resp return resp
# Internal: get a response plus following text from the server. # Internal: get a response plus following text from the server.
def _getlongresp(self): def _getlongresp(self):
resp = self._getresp() resp = self._getresp()
list = []; octets = 0 list = []; octets = 0
line, o = self._getline() line, o = self._getline()
while line != '.': while line != '.':
if line[:2] == '..': if line[:2] == '..':
o = o-1 o = o-1
line = line[1:] line = line[1:]
octets = octets + o octets = octets + o
list.append(line) list.append(line)
line, o = self._getline() line, o = self._getline()
return resp, list, octets return resp, list, octets
# Internal: send a command and get the response # Internal: send a command and get the response
def _shortcmd(self, line): def _shortcmd(self, line):
self._putcmd(line) self._putcmd(line)
return self._getresp() return self._getresp()
# Internal: send a command and get the response plus following text # Internal: send a command and get the response plus following text
def _longcmd(self, line): def _longcmd(self, line):
self._putcmd(line) self._putcmd(line)
return self._getlongresp() return self._getlongresp()
# These can be useful: # These can be useful:
def getwelcome(self): def getwelcome(self):
return self.welcome return self.welcome
def set_debuglevel(self, level): def set_debuglevel(self, level):
self._debugging = level self._debugging = level
# Here are all the POP commands: # Here are all the POP commands:
def user(self, user): def user(self, user):
"""Send user name, return response """Send user name, return response
(should indicate password required).
"""
return self._shortcmd('USER %s' % user)
(should indicate password required).
"""
return self._shortcmd('USER %s' % user)
def pass_(self, pswd):
"""Send password, return response
(response includes message count, mailbox size).
NB: mailbox is locked by server from here to 'quit()' def pass_(self, pswd):
""" """Send password, return response
return self._shortcmd('PASS %s' % pswd)
(response includes message count, mailbox size).
def stat(self): NB: mailbox is locked by server from here to 'quit()'
"""Get mailbox status. """
return self._shortcmd('PASS %s' % pswd)
Result is tuple of 2 ints (message count, mailbox size)
"""
retval = self._shortcmd('STAT')
rets = string.split(retval)
#if self._debugging: print '*stat*', `rets`
numMessages = string.atoi(rets[1])
sizeMessages = string.atoi(rets[2])
return (numMessages, sizeMessages)
def list(self, which=None): def stat(self):
"""Request listing, return result. """Get mailbox status.
Result without a message number argument is in form Result is tuple of 2 ints (message count, mailbox size)
['response', ['mesg_num octets', ...]]. """
retval = self._shortcmd('STAT')
rets = string.split(retval)
#if self._debugging: print '*stat*', `rets`
numMessages = string.atoi(rets[1])
sizeMessages = string.atoi(rets[2])
return (numMessages, sizeMessages)
Result when a message number argument is given is a
single response: the "scan listing" for that message.
"""
if which:
return self._shortcmd('LIST %s' % which)
return self._longcmd('LIST')
def list(self, which=None):
"""Request listing, return result.
def retr(self, which): Result without a message number argument is in form
"""Retrieve whole message number 'which'. ['response', ['mesg_num octets', ...]].
Result is in form ['response', ['line', ...], octets]. Result when a message number argument is given is a
""" single response: the "scan listing" for that message.
return self._longcmd('RETR %s' % which) """
if which:
return self._shortcmd('LIST %s' % which)
return self._longcmd('LIST')
def dele(self, which): def retr(self, which):
"""Delete message number 'which'. """Retrieve whole message number 'which'.
Result is 'response'. Result is in form ['response', ['line', ...], octets].
""" """
return self._shortcmd('DELE %s' % which) return self._longcmd('RETR %s' % which)
def noop(self): def dele(self, which):
"""Does nothing. """Delete message number 'which'.
One supposes the response indicates the server is alive.
"""
return self._shortcmd('NOOP')
Result is 'response'.
"""
return self._shortcmd('DELE %s' % which)
def rset(self):
"""Not sure what this does."""
return self._shortcmd('RSET')
def noop(self):
"""Does nothing.
def quit(self): One supposes the response indicates the server is alive.
"""Signoff: commit changes on server, unlock mailbox, close connection.""" """
try: return self._shortcmd('NOOP')
resp = self._shortcmd('QUIT')
except error_proto, val:
resp = val
self.file.close()
self.sock.close()
del self.file, self.sock
return resp
#__del__ = quit
def rset(self):
"""Not sure what this does."""
return self._shortcmd('RSET')
# optional commands:
def rpop(self, user): def quit(self):
"""Not sure what this does.""" """Signoff: commit changes on server, unlock mailbox, close connection."""
return self._shortcmd('RPOP %s' % user) try:
resp = self._shortcmd('QUIT')
except error_proto, val:
resp = val
self.file.close()
self.sock.close()
del self.file, self.sock
return resp
#__del__ = quit
timestamp = regex.compile('\+OK.*\(<[^>]+>\)')
def apop(self, user, secret): # optional commands:
"""Authorisation
- only possible if server has supplied a timestamp in initial greeting.
Args: def rpop(self, user):
user - mailbox user; """Not sure what this does."""
secret - secret shared between client and server. return self._shortcmd('RPOP %s' % user)
NB: mailbox is locked by server from here to 'quit()'
"""
if self.timestamp.match(self.welcome) <= 0:
raise error_proto('-ERR APOP not supported by server')
import md5
digest = md5.new(self.timestamp.group(1)+secret).digest()
digest = string.join(map(lambda x:'%02x'%ord(x), digest), '')
return self._shortcmd('APOP %s %s' % (user, digest))
timestamp = regex.compile('\+OK.*\(<[^>]+>\)')
def top(self, which, howmuch): def apop(self, user, secret):
"""Retrieve message header of message number 'which' """Authorisation
and first 'howmuch' lines of message body.
Result is in form ['response', ['line', ...], octets]. - only possible if server has supplied a timestamp in initial greeting.
"""
return self._longcmd('TOP %s %s' % (which, howmuch))
Args:
user - mailbox user;
secret - secret shared between client and server.
def uidl(self, which=None): NB: mailbox is locked by server from here to 'quit()'
"""Return message digest (unique id) list. """
if self.timestamp.match(self.welcome) <= 0:
raise error_proto('-ERR APOP not supported by server')
import md5
digest = md5.new(self.timestamp.group(1)+secret).digest()
digest = string.join(map(lambda x:'%02x'%ord(x), digest), '')
return self._shortcmd('APOP %s %s' % (user, digest))
def top(self, which, howmuch):
"""Retrieve message header of message number 'which'
and first 'howmuch' lines of message body.
Result is in form ['response', ['line', ...], octets].
"""
return self._longcmd('TOP %s %s' % (which, howmuch))
def uidl(self, which=None):
"""Return message digest (unique id) list.
If 'which', result contains unique id for that message
in the form 'response mesgnum uid', otherwise result is
the list ['response', ['mesgnum uid', ...], octets]
"""
if which:
return self._shortcmd('UIDL %s' % which)
return self._longcmd('UIDL')
If 'which', result contains unique id for that message
in the form 'response mesgnum uid', otherwise result is
the list ['response', ['mesgnum uid', ...], octets]
"""
if which:
return self._shortcmd('UIDL %s' % which)
return self._longcmd('UIDL')
if __name__ == "__main__": if __name__ == "__main__":
a = POP3(TESTSERVER) a = POP3(TESTSERVER)
print a.getwelcome() print a.getwelcome()
a.user(TESTACCOUNT) a.user(TESTACCOUNT)
a.pass_(TESTPASSWORD) a.pass_(TESTPASSWORD)
a.list() a.list()
(numMsgs, totalSize) = a.stat() (numMsgs, totalSize) = a.stat()
for i in range(1, numMsgs + 1): for i in range(1, numMsgs + 1):
(header, msg, octets) = a.retr(i) (header, msg, octets) = a.retr(i)
print "Message ", `i`, ':' print "Message ", `i`, ':'
for line in msg: for line in msg:
print ' ' + line print ' ' + line
print '-----------------------' print '-----------------------'
a.quit() a.quit()

View file

@ -129,7 +129,7 @@ class _posixfile_:
l_flags = fcntl.fcntl(file.fileno(), FCNTL.F_SETFL, l_flags) l_flags = fcntl.fcntl(file.fileno(), FCNTL.F_SETFL, l_flags)
if 'c' in which: if 'c' in which:
arg = ('!' not in which) # 0 is don't, 1 is do close on exec arg = ('!' not in which) # 0 is don't, 1 is do close on exec
l_flags = fcntl.fcntl(file.fileno(), FCNTL.F_SETFD, arg) l_flags = fcntl.fcntl(file.fileno(), FCNTL.F_SETFD, arg)
@ -142,7 +142,7 @@ class _posixfile_:
if FCNTL.O_NDELAY & l_flags: which = which + 'n' if FCNTL.O_NDELAY & l_flags: which = which + 'n'
if FCNTL.O_SYNC & l_flags: which = which + 's' if FCNTL.O_SYNC & l_flags: which = which + 's'
return which return which
def lock(self, how, *args): def lock(self, how, *args):
import struct, fcntl, FCNTL import struct, fcntl, FCNTL
@ -176,7 +176,7 @@ class _posixfile_:
'freebsd2', 'freebsd3', 'freebsd4', 'freebsd5', 'freebsd2', 'freebsd3', 'freebsd4', 'freebsd5',
'bsdos2', 'bsdos3', 'bsdos4'): 'bsdos2', 'bsdos3', 'bsdos4'):
flock = struct.pack('lxxxxlxxxxlhh', \ flock = struct.pack('lxxxxlxxxxlhh', \
l_start, l_len, os.getpid(), l_type, l_whence) l_start, l_len, os.getpid(), l_type, l_whence)
elif sys.platform in ['aix3', 'aix4']: elif sys.platform in ['aix3', 'aix4']:
flock = struct.pack('hhlllii', \ flock = struct.pack('hhlllii', \
l_type, l_whence, l_start, l_len, 0, 0, 0) l_type, l_whence, l_start, l_len, 0, 0, 0)

View file

@ -55,7 +55,7 @@ def join(a, *p):
# Trailing '/'es are stripped from head unless it is the root. # Trailing '/'es are stripped from head unless it is the root.
def split(p): def split(p):
"""Split a pathname. Returns tuple "(head, tail)" where "tail" is """Split a pathname. Returns tuple "(head, tail)" where "tail" is
everything after the final slash. Either part may be empty.""" everything after the final slash. Either part may be empty."""
i = p.rfind('/') + 1 i = p.rfind('/') + 1
head, tail = p[:i], p[i:] head, tail = p[:i], p[i:]
@ -93,7 +93,7 @@ def splitext(p):
# path. Useful on DOS/Windows/NT; on Unix, the drive is always empty. # path. Useful on DOS/Windows/NT; on Unix, the drive is always empty.
def splitdrive(p): def splitdrive(p):
"""Split a pathname into drive and path. On Posix, drive is always """Split a pathname into drive and path. On Posix, drive is always
empty.""" empty."""
return '', p return '', p
@ -220,7 +220,7 @@ def sameopenfile(fp1, fp2):
def samestat(s1, s2): def samestat(s1, s2):
"""Test whether two stat buffers reference the same file""" """Test whether two stat buffers reference the same file"""
return s1[stat.ST_INO] == s2[stat.ST_INO] and \ return s1[stat.ST_INO] == s2[stat.ST_INO] and \
s1[stat.ST_DEV] == s2[stat.ST_DEV] s1[stat.ST_DEV] == s2[stat.ST_DEV]
# Is a path a mount point? # Is a path a mount point?
@ -253,7 +253,7 @@ def ismount(path):
# or to impose a different order of visiting. # or to impose a different order of visiting.
def walk(top, func, arg): def walk(top, func, arg):
"""walk(top,func,arg) calls func(arg, d, files) for each directory "d" """walk(top,func,arg) calls func(arg, d, files) for each directory "d"
in the tree rooted at "top" (including "top" itself). "files" is a list in the tree rooted at "top" (including "top" itself). "files" is a list
of all the files and subdirs in directory "d". of all the files and subdirs in directory "d".
""" """
@ -263,10 +263,10 @@ def walk(top, func, arg):
return return
func(arg, top, names) func(arg, top, names)
for name in names: for name in names:
name = join(top, name) name = join(top, name)
st = os.lstat(name) st = os.lstat(name)
if stat.S_ISDIR(st[stat.ST_MODE]): if stat.S_ISDIR(st[stat.ST_MODE]):
walk(name, func, arg) walk(name, func, arg)
# Expand paths beginning with '~' or '~user'. # Expand paths beginning with '~' or '~user'.
@ -279,7 +279,7 @@ def walk(top, func, arg):
# variable expansion.) # variable expansion.)
def expanduser(path): def expanduser(path):
"""Expand ~ and ~user constructions. If user or $HOME is unknown, """Expand ~ and ~user constructions. If user or $HOME is unknown,
do nothing.""" do nothing."""
if path[:1] != '~': if path[:1] != '~':
return path return path
@ -349,7 +349,7 @@ def normpath(path):
for comp in comps: for comp in comps:
if comp in ('', '.'): if comp in ('', '.'):
continue continue
if (comp != '..' or (not initial_slash and not new_comps) or if (comp != '..' or (not initial_slash and not new_comps) or
(new_comps and new_comps[-1] == '..')): (new_comps and new_comps[-1] == '..')):
new_comps.append(comp) new_comps.append(comp)
elif new_comps: elif new_comps:

View file

@ -44,7 +44,7 @@ below. If the ordinary character is not on the list, then the
resulting RE will match the second character. resulting RE will match the second character.
\\number Matches the contents of the group of the same number. \\number Matches the contents of the group of the same number.
\\A Matches only at the start of the string. \\A Matches only at the start of the string.
\\Z Matches only at the end of the string. \\Z Matches only at the end of the string.
\\b Matches the empty string, but only at the start or end of a word. \\b Matches the empty string, but only at the start or end of a word.
\\B Matches the empty string, but not at the start or end of a word. \\B Matches the empty string, but not at the start or end of a word.
\\d Matches any decimal digit; equivalent to the set [0-9]. \\d Matches any decimal digit; equivalent to the set [0-9].
@ -55,7 +55,7 @@ resulting RE will match the second character.
With LOCALE, it will match the set [0-9_] plus characters defined With LOCALE, it will match the set [0-9_] plus characters defined
as letters for the current locale. as letters for the current locale.
\\W Matches the complement of \\w. \\W Matches the complement of \\w.
\\\\ Matches a literal backslash. \\\\ Matches a literal backslash.
This module exports the following functions: This module exports the following functions:
match Match a regular expression pattern to the beginning of a string. match Match a regular expression pattern to the beginning of a string.
@ -100,8 +100,8 @@ from pcre import *
I = IGNORECASE I = IGNORECASE
L = LOCALE L = LOCALE
M = MULTILINE M = MULTILINE
S = DOTALL S = DOTALL
X = VERBOSE X = VERBOSE
# #
@ -125,7 +125,7 @@ def _cachecompile(pattern, flags=0):
def match(pattern, string, flags=0): def match(pattern, string, flags=0):
"""match (pattern, string[, flags]) -> MatchObject or None """match (pattern, string[, flags]) -> MatchObject or None
If zero or more characters at the beginning of string match the If zero or more characters at the beginning of string match the
regular expression pattern, return a corresponding MatchObject regular expression pattern, return a corresponding MatchObject
instance. Return None if the string does not match the pattern; instance. Return None if the string does not match the pattern;
@ -135,12 +135,12 @@ def match(pattern, string, flags=0):
search() instead. search() instead.
""" """
return _cachecompile(pattern, flags).match(string) return _cachecompile(pattern, flags).match(string)
def search(pattern, string, flags=0): def search(pattern, string, flags=0):
"""search (pattern, string[, flags]) -> MatchObject or None """search (pattern, string[, flags]) -> MatchObject or None
Scan through string looking for a location where the regular Scan through string looking for a location where the regular
expression pattern produces a match, and return a corresponding expression pattern produces a match, and return a corresponding
MatchObject instance. Return None if no position in the string MatchObject instance. Return None if no position in the string
@ -149,10 +149,10 @@ def search(pattern, string, flags=0):
""" """
return _cachecompile(pattern, flags).search(string) return _cachecompile(pattern, flags).search(string)
def sub(pattern, repl, string, count=0): def sub(pattern, repl, string, count=0):
"""sub(pattern, repl, string[, count=0]) -> string """sub(pattern, repl, string[, count=0]) -> string
Return the string obtained by replacing the leftmost Return the string obtained by replacing the leftmost
non-overlapping occurrences of pattern in string by the non-overlapping occurrences of pattern in string by the
replacement repl. If the pattern isn't found, string is returned replacement repl. If the pattern isn't found, string is returned
@ -177,7 +177,7 @@ def sub(pattern, repl, string, count=0):
def subn(pattern, repl, string, count=0): def subn(pattern, repl, string, count=0):
"""subn(pattern, repl, string[, count=0]) -> (string, num substitutions) """subn(pattern, repl, string[, count=0]) -> (string, num substitutions)
Perform the same operation as sub(), but return a tuple Perform the same operation as sub(), but return a tuple
(new_string, number_of_subs_made). (new_string, number_of_subs_made).
@ -185,10 +185,10 @@ def subn(pattern, repl, string, count=0):
if type(pattern) == type(''): if type(pattern) == type(''):
pattern = _cachecompile(pattern) pattern = _cachecompile(pattern)
return pattern.subn(repl, string, count) return pattern.subn(repl, string, count)
def split(pattern, string, maxsplit=0): def split(pattern, string, maxsplit=0):
"""split(pattern, string[, maxsplit=0]) -> list of strings """split(pattern, string[, maxsplit=0]) -> list of strings
Split string by the occurrences of pattern. If capturing Split string by the occurrences of pattern. If capturing
parentheses are used in pattern, then the text of all groups in parentheses are used in pattern, then the text of all groups in
the pattern are also returned as part of the resulting list. If the pattern are also returned as part of the resulting list. If
@ -203,7 +203,7 @@ def split(pattern, string, maxsplit=0):
def findall(pattern, string): def findall(pattern, string):
"""findall(pattern, string) -> list """findall(pattern, string) -> list
Return a list of all non-overlapping matches of pattern in Return a list of all non-overlapping matches of pattern in
string. If one or more groups are present in the pattern, return a string. If one or more groups are present in the pattern, return a
list of groups; this will be a list of tuples if the pattern has list of groups; this will be a list of tuples if the pattern has
@ -216,7 +216,7 @@ def findall(pattern, string):
def escape(pattern): def escape(pattern):
"""escape(string) -> string """escape(string) -> string
Return string with all non-alphanumerics backslashed; this is Return string with all non-alphanumerics backslashed; this is
useful if you want to match an arbitrary literal string that may useful if you want to match an arbitrary literal string that may
have regular expression metacharacters in it. have regular expression metacharacters in it.
@ -242,7 +242,7 @@ def compile(pattern, flags=0):
groupindex={} groupindex={}
code=pcre_compile(pattern, flags, groupindex) code=pcre_compile(pattern, flags, groupindex)
return RegexObject(pattern, flags, code, groupindex) return RegexObject(pattern, flags, code, groupindex)
# #
# Class definitions # Class definitions
@ -258,18 +258,18 @@ class RegexObject:
subn Same as sub, but also return the number of substitutions made. subn Same as sub, but also return the number of substitutions made.
split Split a string by the occurrences of the pattern. split Split a string by the occurrences of the pattern.
findall Find all occurrences of the pattern in a string. findall Find all occurrences of the pattern in a string.
""" """
def __init__(self, pattern, flags, code, groupindex): def __init__(self, pattern, flags, code, groupindex):
self.code = code self.code = code
self.flags = flags self.flags = flags
self.pattern = pattern self.pattern = pattern
self.groupindex = groupindex self.groupindex = groupindex
def search(self, string, pos=0, endpos=None): def search(self, string, pos=0, endpos=None):
"""search(string[, pos][, endpos]) -> MatchObject or None """search(string[, pos][, endpos]) -> MatchObject or None
Scan through string looking for a location where this regular Scan through string looking for a location where this regular
expression produces a match, and return a corresponding expression produces a match, and return a corresponding
MatchObject instance. Return None if no position in the string MatchObject instance. Return None if no position in the string
@ -277,24 +277,24 @@ class RegexObject:
a zero-length match at some point in the string. The optional a zero-length match at some point in the string. The optional
pos and endpos parameters have the same meaning as for the pos and endpos parameters have the same meaning as for the
match() method. match() method.
""" """
if endpos is None or endpos>len(string): if endpos is None or endpos>len(string):
endpos=len(string) endpos=len(string)
if endpos<pos: endpos=pos if endpos<pos: endpos=pos
regs = self.code.match(string, pos, endpos, 0) regs = self.code.match(string, pos, endpos, 0)
if regs is None: if regs is None:
return None return None
self._num_regs=len(regs) self._num_regs=len(regs)
return MatchObject(self, return MatchObject(self,
string, string,
pos, endpos, pos, endpos,
regs) regs)
def match(self, string, pos=0, endpos=None): def match(self, string, pos=0, endpos=None):
"""match(string[, pos][, endpos]) -> MatchObject or None """match(string[, pos][, endpos]) -> MatchObject or None
If zero or more characters at the beginning of string match If zero or more characters at the beginning of string match
this regular expression, return a corresponding MatchObject this regular expression, return a corresponding MatchObject
instance. Return None if the string does not match the instance. Return None if the string does not match the
@ -316,7 +316,7 @@ class RegexObject:
searched for a match. searched for a match.
""" """
if endpos is None or endpos>len(string): if endpos is None or endpos>len(string):
endpos=len(string) endpos=len(string)
if endpos<pos: endpos=pos if endpos<pos: endpos=pos
regs = self.code.match(string, pos, endpos, ANCHORED) regs = self.code.match(string, pos, endpos, ANCHORED)
@ -327,23 +327,23 @@ class RegexObject:
string, string,
pos, endpos, pos, endpos,
regs) regs)
def sub(self, repl, string, count=0): def sub(self, repl, string, count=0):
"""sub(repl, string[, count=0]) -> string """sub(repl, string[, count=0]) -> string
Return the string obtained by replacing the leftmost Return the string obtained by replacing the leftmost
non-overlapping occurrences of the compiled pattern in string non-overlapping occurrences of the compiled pattern in string
by the replacement repl. If the pattern isn't found, string is by the replacement repl. If the pattern isn't found, string is
returned unchanged. returned unchanged.
Identical to the sub() function, using the compiled pattern. Identical to the sub() function, using the compiled pattern.
""" """
return self.subn(repl, string, count)[0] return self.subn(repl, string, count)[0]
def subn(self, repl, source, count=0): def subn(self, repl, source, count=0):
"""subn(repl, string[, count=0]) -> tuple """subn(repl, string[, count=0]) -> tuple
Perform the same operation as sub(), but return a tuple Perform the same operation as sub(), but return a tuple
(new_string, number_of_subs_made). (new_string, number_of_subs_made).
@ -399,17 +399,17 @@ class RegexObject:
n = n + 1 n = n + 1
append(source[pos:]) append(source[pos:])
return (string.join(results, ''), n) return (string.join(results, ''), n)
def split(self, source, maxsplit=0): def split(self, source, maxsplit=0):
"""split(source[, maxsplit=0]) -> list of strings """split(source[, maxsplit=0]) -> list of strings
Split string by the occurrences of the compiled pattern. If Split string by the occurrences of the compiled pattern. If
capturing parentheses are used in the pattern, then the text capturing parentheses are used in the pattern, then the text
of all groups in the pattern are also returned as part of the of all groups in the pattern are also returned as part of the
resulting list. If maxsplit is nonzero, at most maxsplit resulting list. If maxsplit is nonzero, at most maxsplit
splits occur, and the remainder of the string is returned as splits occur, and the remainder of the string is returned as
the final element of the list. the final element of the list.
""" """
if maxsplit < 0: if maxsplit < 0:
raise error, "negative split count" raise error, "negative split count"
@ -449,7 +449,7 @@ class RegexObject:
def findall(self, source): def findall(self, source):
"""findall(source) -> list """findall(source) -> list
Return a list of all non-overlapping matches of the compiled Return a list of all non-overlapping matches of the compiled
pattern in string. If one or more groups are present in the pattern in string. If one or more groups are present in the
pattern, return a list of groups; this will be a list of pattern, return a list of groups; this will be a list of
@ -487,7 +487,7 @@ class RegexObject:
def __getinitargs__(self): def __getinitargs__(self):
return (None,None,None,None) # any 4 elements, to work around return (None,None,None,None) # any 4 elements, to work around
# problems with the # problems with the
# pickle/cPickle modules not yet # pickle/cPickle modules not yet
# ignoring the __init__ function # ignoring the __init__ function
def __getstate__(self): def __getstate__(self):
return self.pattern, self.flags, self.groupindex return self.pattern, self.flags, self.groupindex
@ -517,13 +517,13 @@ class MatchObject:
def __init__(self, re, string, pos, endpos, regs): def __init__(self, re, string, pos, endpos, regs):
self.re = re self.re = re
self.string = string self.string = string
self.pos = pos self.pos = pos
self.endpos = endpos self.endpos = endpos
self.regs = regs self.regs = regs
def start(self, g = 0): def start(self, g = 0):
"""start([group=0]) -> int or None """start([group=0]) -> int or None
Return the index of the start of the substring matched by Return the index of the start of the substring matched by
group; group defaults to zero (meaning the whole matched group; group defaults to zero (meaning the whole matched
substring). Return -1 if group exists but did not contribute substring). Return -1 if group exists but did not contribute
@ -536,10 +536,10 @@ class MatchObject:
except (KeyError, TypeError): except (KeyError, TypeError):
raise IndexError, 'group %s is undefined' % `g` raise IndexError, 'group %s is undefined' % `g`
return self.regs[g][0] return self.regs[g][0]
def end(self, g = 0): def end(self, g = 0):
"""end([group=0]) -> int or None """end([group=0]) -> int or None
Return the indices of the end of the substring matched by Return the indices of the end of the substring matched by
group; group defaults to zero (meaning the whole matched group; group defaults to zero (meaning the whole matched
substring). Return -1 if group exists but did not contribute substring). Return -1 if group exists but did not contribute
@ -552,10 +552,10 @@ class MatchObject:
except (KeyError, TypeError): except (KeyError, TypeError):
raise IndexError, 'group %s is undefined' % `g` raise IndexError, 'group %s is undefined' % `g`
return self.regs[g][1] return self.regs[g][1]
def span(self, g = 0): def span(self, g = 0):
"""span([group=0]) -> tuple """span([group=0]) -> tuple
Return the 2-tuple (m.start(group), m.end(group)). Note that Return the 2-tuple (m.start(group), m.end(group)). Note that
if group did not contribute to the match, this is (-1, if group did not contribute to the match, this is (-1,
-1). Group defaults to zero (meaning the whole matched -1). Group defaults to zero (meaning the whole matched
@ -568,10 +568,10 @@ class MatchObject:
except (KeyError, TypeError): except (KeyError, TypeError):
raise IndexError, 'group %s is undefined' % `g` raise IndexError, 'group %s is undefined' % `g`
return self.regs[g] return self.regs[g]
def groups(self, default=None): def groups(self, default=None):
"""groups([default=None]) -> tuple """groups([default=None]) -> tuple
Return a tuple containing all the subgroups of the match, from Return a tuple containing all the subgroups of the match, from
1 up to however many groups are in the pattern. The default 1 up to however many groups are in the pattern. The default
argument is used for groups that did not participate in the argument is used for groups that did not participate in the
@ -589,7 +589,7 @@ class MatchObject:
def group(self, *groups): def group(self, *groups):
"""group([group1, group2, ...]) -> string or tuple """group([group1, group2, ...]) -> string or tuple
Return one or more subgroups of the match. If there is a Return one or more subgroups of the match. If there is a
single argument, the result is a single string; if there are single argument, the result is a single string; if there are
multiple arguments, the result is a tuple with one item per multiple arguments, the result is a tuple with one item per
@ -636,7 +636,7 @@ class MatchObject:
def groupdict(self, default=None): def groupdict(self, default=None):
"""groupdict([default=None]) -> dictionary """groupdict([default=None]) -> dictionary
Return a dictionary containing all the named subgroups of the Return a dictionary containing all the named subgroups of the
match, keyed by the subgroup name. The default argument is match, keyed by the subgroup name. The default argument is
used for groups that did not participate in the match. used for groups that did not participate in the match.

View file

@ -11,7 +11,7 @@
# Copyright 1994, by InfoSeek Corporation, all rights reserved. # Copyright 1994, by InfoSeek Corporation, all rights reserved.
# Written by James Roskind # Written by James Roskind
# #
# Permission to use, copy, modify, and distribute this Python software # Permission to use, copy, modify, and distribute this Python software
# and its associated documentation for any purpose (subject to the # and its associated documentation for any purpose (subject to the
# restriction in the following sentence) without fee is hereby granted, # restriction in the following sentence) without fee is hereby granted,
@ -24,7 +24,7 @@
# to remain in Python, compiled Python, or other languages (such as C) # to remain in Python, compiled Python, or other languages (such as C)
# wherein the modified or derived code is exclusively imported into a # wherein the modified or derived code is exclusively imported into a
# Python module. # Python module.
# #
# INFOSEEK CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS # INFOSEEK CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND # SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS. IN NO EVENT SHALL INFOSEEK CORPORATION BE LIABLE FOR ANY # FITNESS. IN NO EVENT SHALL INFOSEEK CORPORATION BE LIABLE FOR ANY
@ -41,12 +41,12 @@ import time
import marshal import marshal
# Sample timer for use with # Sample timer for use with
#i_count = 0 #i_count = 0
#def integer_timer(): #def integer_timer():
# global i_count # global i_count
# i_count = i_count + 1 # i_count = i_count + 1
# return i_count # return i_count
#itimes = integer_timer # replace with C coded timer returning integers #itimes = integer_timer # replace with C coded timer returning integers
#************************************************************************** #**************************************************************************
@ -57,515 +57,515 @@ import marshal
# simplified user interface # simplified user interface
def run(statement, *args): def run(statement, *args):
prof = Profile() prof = Profile()
try: try:
prof = prof.run(statement) prof = prof.run(statement)
except SystemExit: except SystemExit:
pass pass
if args: if args:
prof.dump_stats(args[0]) prof.dump_stats(args[0])
else: else:
return prof.print_stats() return prof.print_stats()
# print help # print help
def help(): def help():
for dirname in sys.path: for dirname in sys.path:
fullname = os.path.join(dirname, 'profile.doc') fullname = os.path.join(dirname, 'profile.doc')
if os.path.exists(fullname): if os.path.exists(fullname):
sts = os.system('${PAGER-more} '+fullname) sts = os.system('${PAGER-more} '+fullname)
if sts: print '*** Pager exit status:', sts if sts: print '*** Pager exit status:', sts
break break
else: else:
print 'Sorry, can\'t find the help file "profile.doc"', print 'Sorry, can\'t find the help file "profile.doc"',
print 'along the Python search path' print 'along the Python search path'
class Profile: class Profile:
"""Profiler class. """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. self.cur is always a tuple. Each such tuple corresponds to a stack
It is used so that a function call will not have to access the frame that is currently active (self.cur[-2]). The following are the
timing data for the parent frame. definitions of its members. We use this external "parallel stack" to
[ 1] = Total time spent in this frame's function, excluding time in avoid contaminating the program that we are profiling. (old profiler
subfunctions used to write into the frames local dictionary!!) Derived classes
[ 2] = Cumulative time spent in this frame's function, including time in can change the definition of some entries, as long as they leave
all subfunctions to this frame. [-2:] intact.
[-3] = Name of the function that corresponds to this frame.
[-2] = Actual frame that we correspond to (used to sync exception handling)
[-1] = Our parent 6-tuple (corresponds to frame.f_back)
Timing data for each function is stored as a 5-tuple in the dictionary [ 0] = Time that needs to be charged to the parent frame's function.
self.timings[]. The index is always the name stored in self.cur[4]. It is used so that a function call will not have to access the
The following are the definitions of the members: 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 corresponds to this frame.
[-2] = Actual frame that we correspond to (used to sync exception handling)
[-1] = Our parent 6-tuple (corresponds to frame.f_back)
[0] = The number of times this function was called, not counting direct Timing data for each function is stored as a 5-tuple in the dictionary
or indirect recursion, self.timings[]. The index is always the name stored in self.cur[4].
[1] = Number of times this function appears on the stack, minus one The following are the definitions of the members:
[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): [0] = The number of times this function was called, not counting direct
self.timings = {} or indirect recursion,
self.cur = None [1] = Number of times this function appears on the stack, minus one
self.cmd = "" [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.
"""
self.dispatch = { \ def __init__(self, timer=None):
'call' : self.trace_dispatch_call, \ self.timings = {}
'return' : self.trace_dispatch_return, \ self.cur = None
'exception': self.trace_dispatch_exception, \ self.cmd = ""
}
if not timer: self.dispatch = { \
if os.name == 'mac': 'call' : self.trace_dispatch_call, \
import MacOS 'return' : self.trace_dispatch_return, \
self.timer = MacOS.GetTicks 'exception': self.trace_dispatch_exception, \
self.dispatcher = self.trace_dispatch_mac }
self.get_time = self.get_time_mac
elif hasattr(time, 'clock'): if not timer:
self.timer = time.clock if os.name == 'mac':
self.dispatcher = self.trace_dispatch_i import MacOS
elif hasattr(os, 'times'): self.timer = MacOS.GetTicks
self.timer = os.times self.dispatcher = self.trace_dispatch_mac
self.dispatcher = self.trace_dispatch self.get_time = self.get_time_mac
else: elif hasattr(time, 'clock'):
self.timer = time.time self.timer = time.clock
self.dispatcher = self.trace_dispatch_i self.dispatcher = self.trace_dispatch_i
else: elif hasattr(os, 'times'):
self.timer = timer self.timer = os.times
t = self.timer() # test out timer function self.dispatcher = self.trace_dispatch
try: else:
if len(t) == 2: self.timer = time.time
self.dispatcher = self.trace_dispatch self.dispatcher = self.trace_dispatch_i
else: else:
self.dispatcher = self.trace_dispatch_l self.timer = timer
except TypeError: t = self.timer() # test out timer function
self.dispatcher = self.trace_dispatch_i try:
self.t = self.get_time() if len(t) == 2:
self.simulate_call('profiler') self.dispatcher = self.trace_dispatch
else:
self.dispatcher = self.trace_dispatch_l
except TypeError:
self.dispatcher = self.trace_dispatch_i
self.t = self.get_time()
self.simulate_call('profiler')
def get_time(self): # slow simulation of method to acquire time def get_time(self): # slow simulation of method to acquire time
t = self.timer() t = self.timer()
if type(t) == type(()) or type(t) == type([]): if type(t) == type(()) or type(t) == type([]):
t = reduce(lambda x,y: x+y, t, 0) t = reduce(lambda x,y: x+y, t, 0)
return t return t
def get_time_mac(self):
return self.timer()/60.0
# Heavily optimized dispatch routine for os.times() timer def get_time_mac(self):
return self.timer()/60.0
def trace_dispatch(self, frame, event, arg): # Heavily optimized dispatch routine for os.times() timer
t = self.timer()
t = t[0] + t[1] - self.t # No Calibration constant
# t = t[0] + t[1] - self.t - .00053 # Calibration constant
if self.dispatch[event](frame,t): def trace_dispatch(self, frame, event, arg):
t = self.timer() t = self.timer()
self.t = t[0] + t[1] t = t[0] + t[1] - self.t # No Calibration constant
else: # t = t[0] + t[1] - self.t - .00053 # Calibration constant
r = self.timer()
self.t = r[0] + r[1] - t # put back unrecorded delta if self.dispatch[event](frame,t):
return t = self.timer()
self.t = t[0] + t[1]
else:
r = self.timer()
self.t = r[0] + r[1] - t # put back unrecorded delta
return
# Dispatch routine for best timer program (return = scalar integer) # Dispatch routine for best timer program (return = scalar integer)
def trace_dispatch_i(self, frame, event, arg): def trace_dispatch_i(self, frame, event, arg):
t = self.timer() - self.t # - 1 # Integer calibration constant t = self.timer() - self.t # - 1 # Integer calibration constant
if self.dispatch[event](frame,t): if self.dispatch[event](frame,t):
self.t = self.timer() self.t = self.timer()
else: else:
self.t = self.timer() - t # put back unrecorded delta self.t = self.timer() - t # put back unrecorded delta
return return
# Dispatch routine for macintosh (timer returns time in ticks of 1/60th second)
def trace_dispatch_mac(self, frame, event, arg): # Dispatch routine for macintosh (timer returns time in ticks of 1/60th second)
t = self.timer()/60.0 - self.t # - 1 # Integer calibration constant
if self.dispatch[event](frame,t): def trace_dispatch_mac(self, frame, event, arg):
self.t = self.timer()/60.0 t = self.timer()/60.0 - self.t # - 1 # Integer calibration constant
else: if self.dispatch[event](frame,t):
self.t = self.timer()/60.0 - t # put back unrecorded delta self.t = self.timer()/60.0
return else:
self.t = self.timer()/60.0 - t # put back unrecorded delta
return
# SLOW generic dispatch routine for timer returning lists of numbers # SLOW generic dispatch routine for timer returning lists of numbers
def trace_dispatch_l(self, frame, event, arg): def trace_dispatch_l(self, frame, event, arg):
t = self.get_time() - self.t t = self.get_time() - self.t
if self.dispatch[event](frame,t): if self.dispatch[event](frame,t):
self.t = self.get_time() self.t = self.get_time()
else: else:
self.t = self.get_time()-t # put back unrecorded delta self.t = self.get_time()-t # put back unrecorded delta
return return
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 (not rframe is frame) and rcur: if (not rframe is frame) and rcur:
return self.trace_dispatch_return(rframe, t) return self.trace_dispatch_return(rframe, t)
return 0 return 0
def trace_dispatch_call(self, frame, t): def trace_dispatch_call(self, frame, t):
fcode = frame.f_code fcode = frame.f_code
fn = (fcode.co_filename, fcode.co_firstlineno, fcode.co_name) fn = (fcode.co_filename, fcode.co_firstlineno, fcode.co_name)
self.cur = (t, 0, 0, fn, frame, self.cur) self.cur = (t, 0, 0, fn, frame, self.cur)
if self.timings.has_key(fn): if self.timings.has_key(fn):
cc, ns, tt, ct, callers = self.timings[fn] cc, ns, tt, ct, callers = self.timings[fn]
self.timings[fn] = cc, ns + 1, tt, ct, callers self.timings[fn] = cc, ns + 1, tt, ct, callers
else: else:
self.timings[fn] = 0, 0, 0, 0, {} self.timings[fn] = 0, 0, 0, 0, {}
return 1 return 1
def trace_dispatch_return(self, frame, t): def trace_dispatch_return(self, frame, t):
# if not frame is self.cur[-2]: raise "Bad return", self.cur[3] # if not frame is self.cur[-2]: raise "Bad return", self.cur[3]
# Prefix "r" means part of the Returning or exiting frame # Prefix "r" means part of the Returning or exiting frame
# Prefix "p" means part of the Previous or older frame # Prefix "p" means part of the Previous or older frame
rt, rtt, rct, rfn, frame, rcur = self.cur rt, rtt, rct, rfn, frame, rcur = self.cur
rtt = rtt + t rtt = rtt + t
sft = rtt + rct sft = rtt + rct
pt, ptt, pct, pfn, pframe, pcur = rcur pt, ptt, pct, pfn, pframe, pcur = rcur
self.cur = pt, ptt+rt, pct+sft, pfn, pframe, pcur self.cur = pt, ptt+rt, pct+sft, pfn, pframe, pcur
cc, ns, tt, ct, callers = self.timings[rfn] cc, ns, tt, ct, callers = self.timings[rfn]
if not ns: if not ns:
ct = ct + sft ct = ct + sft
cc = cc + 1 cc = cc + 1
if callers.has_key(pfn): if callers.has_key(pfn):
callers[pfn] = callers[pfn] + 1 # hack: gather more callers[pfn] = callers[pfn] + 1 # hack: gather more
# stats such as the amount of time added to ct courtesy # stats such as the amount of time added to ct courtesy
# of this specific call, and the contribution to cc # of this specific call, and the contribution to cc
# courtesy of this call. # courtesy of this call.
else: else:
callers[pfn] = 1 callers[pfn] = 1
self.timings[rfn] = cc, ns - 1, tt+rtt, ct, callers self.timings[rfn] = cc, ns - 1, tt+rtt, ct, callers
return 1 return 1
# The next few function play with self.cmd. By carefully preloading # The next few function play with self.cmd. By carefully preloading
# our parallel stack, we can force the profiled result to include # our parallel stack, we can force the profiled result to include
# an arbitrary string as the name of the calling function. # an arbitrary string as the name of the calling function.
# We use self.cmd as that string, and the resulting stats look # We use self.cmd as that string, and the resulting stats look
# very nice :-). # very nice :-).
def set_cmd(self, cmd): def set_cmd(self, cmd):
if self.cur[-1]: return # already set if self.cur[-1]: return # already set
self.cmd = cmd self.cmd = cmd
self.simulate_call(cmd) self.simulate_call(cmd)
class fake_code: class fake_code:
def __init__(self, filename, line, name): def __init__(self, filename, line, name):
self.co_filename = filename self.co_filename = filename
self.co_line = line self.co_line = line
self.co_name = name self.co_name = name
self.co_firstlineno = 0 self.co_firstlineno = 0
def __repr__(self): def __repr__(self):
return repr((self.co_filename, self.co_line, self.co_name)) return repr((self.co_filename, self.co_line, self.co_name))
class fake_frame: class fake_frame:
def __init__(self, code, prior): def __init__(self, code, prior):
self.f_code = code self.f_code = code
self.f_back = prior self.f_back = prior
def simulate_call(self, name):
code = self.fake_code('profile', 0, name)
if self.cur:
pframe = self.cur[-2]
else:
pframe = None
frame = self.fake_frame(code, pframe)
a = self.dispatch['call'](frame, 0)
return
# collect stats from pending stack, including getting final def simulate_call(self, name):
# timings for self.cmd frame. code = self.fake_code('profile', 0, name)
if self.cur:
def simulate_cmd_complete(self): pframe = self.cur[-2]
t = self.get_time() - self.t else:
while self.cur[-1]: pframe = None
# We *can* cause assertion errors here if frame = self.fake_frame(code, pframe)
# dispatch_trace_return checks for a frame match! a = self.dispatch['call'](frame, 0)
a = self.dispatch['return'](self.cur[-2], t) return
t = 0
self.t = self.get_time() - t
# collect stats from pending stack, including getting final
def print_stats(self): # timings for self.cmd frame.
import pstats
pstats.Stats(self).strip_dirs().sort_stats(-1). \
print_stats()
def dump_stats(self, file): def simulate_cmd_complete(self):
f = open(file, 'wb') t = self.get_time() - self.t
self.create_stats() while self.cur[-1]:
marshal.dump(self.stats, f) # We *can* cause assertion errors here if
f.close() # dispatch_trace_return checks for a frame match!
a = self.dispatch['return'](self.cur[-2], t)
def create_stats(self): t = 0
self.simulate_cmd_complete() self.t = self.get_time() - t
self.snapshot_stats()
def snapshot_stats(self):
self.stats = {}
for func in self.timings.keys():
cc, ns, tt, ct, callers = self.timings[func]
callers = callers.copy()
nc = 0
for func_caller in callers.keys():
nc = nc + callers[func_caller]
self.stats[func] = cc, nc, tt, ct, callers
# The following two methods can be called by clients to use def print_stats(self):
# a profiler to profile a statement, given as a string. import pstats
pstats.Stats(self).strip_dirs().sort_stats(-1). \
def run(self, cmd): print_stats()
import __main__
dict = __main__.__dict__
return self.runctx(cmd, dict, dict)
def runctx(self, cmd, globals, locals):
self.set_cmd(cmd)
sys.setprofile(self.dispatcher)
try:
exec cmd in globals, locals
finally:
sys.setprofile(None)
return self
# This method is more useful to profile a single function call. def dump_stats(self, file):
def runcall(self, func, *args): f = open(file, 'wb')
self.set_cmd(`func`) self.create_stats()
sys.setprofile(self.dispatcher) marshal.dump(self.stats, f)
try: f.close()
return apply(func, args)
finally: def create_stats(self):
sys.setprofile(None) self.simulate_cmd_complete()
self.snapshot_stats()
def snapshot_stats(self):
self.stats = {}
for func in self.timings.keys():
cc, ns, tt, ct, callers = self.timings[func]
callers = callers.copy()
nc = 0
for func_caller in callers.keys():
nc = nc + callers[func_caller]
self.stats[func] = cc, nc, tt, ct, callers
#****************************************************************** # The following two methods can be called by clients to use
# The following calculates the overhead for using a profiler. The # a profiler to profile a statement, given as a string.
# problem is that it takes a fair amount of time for the profiler
# to stop the stopwatch (from the time it receives an event).
# Similarly, there is a delay from the time that the profiler
# re-starts the stopwatch before the user's code really gets to
# continue. The following code tries to measure the difference on
# a per-event basis. The result can the be placed in the
# Profile.dispatch_event() routine for the given platform. Note
# that this difference is only significant if there are a lot of
# events, and relatively little user code per event. For example,
# code with small functions will typically benefit from having the
# profiler calibrated for the current platform. This *could* be
# done on the fly during init() time, but it is not worth the
# effort. Also note that if too large a value specified, then
# execution time on some functions will actually appear as a
# negative number. It is *normal* for some functions (with very
# low call counts) to have such negative stats, even if the
# calibration figure is "correct."
#
# One alternative to profile-time calibration adjustments (i.e.,
# adding in the magic little delta during each event) is to track
# more carefully the number of events (and cumulatively, the number
# of events during sub functions) that are seen. If this were
# done, then the arithmetic could be done after the fact (i.e., at
# display time). Currently, we track only call/return events.
# These values can be deduced by examining the callees and callers
# vectors for each functions. Hence we *can* almost correct the
# internal time figure at print time (note that we currently don't
# track exception event processing counts). Unfortunately, there
# is currently no similar information for cumulative sub-function
# time. It would not be hard to "get all this info" at profiler
# time. Specifically, we would have to extend the tuples to keep
# counts of this in each frame, and then extend the defs of timing
# tuples to include the significant two figures. I'm a bit fearful
# that this additional feature will slow the heavily optimized
# event/time ratio (i.e., the profiler would run slower, fur a very
# low "value added" feature.)
#
# Plugging in the calibration constant doesn't slow down the
# profiler very much, and the accuracy goes way up.
#**************************************************************
def calibrate(self, m):
# Modified by Tim Peters
n = m
s = self.get_time()
while n:
self.simple()
n = n - 1
f = self.get_time()
my_simple = f - s
#print "Simple =", my_simple,
n = m def run(self, cmd):
s = self.get_time() import __main__
while n: dict = __main__.__dict__
self.instrumented() return self.runctx(cmd, dict, dict)
n = n - 1
f = self.get_time()
my_inst = f - s
# print "Instrumented =", my_inst
avg_cost = (my_inst - my_simple)/m
#print "Delta/call =", avg_cost, "(profiler fixup constant)"
return avg_cost
# simulate a program with no profiler activity def runctx(self, cmd, globals, locals):
def simple(self): self.set_cmd(cmd)
a = 1 sys.setprofile(self.dispatcher)
pass try:
exec cmd in globals, locals
finally:
sys.setprofile(None)
return self
# simulate a program with call/return event processing # This method is more useful to profile a single function call.
def instrumented(self): def runcall(self, func, *args):
a = 1 self.set_cmd(`func`)
self.profiler_simulation(a, a, a) sys.setprofile(self.dispatcher)
try:
return apply(func, args)
finally:
sys.setprofile(None)
# simulate an event processing activity (from user's perspective)
def profiler_simulation(self, x, y, z): #******************************************************************
t = self.timer() # The following calculates the overhead for using a profiler. The
## t = t[0] + t[1] # problem is that it takes a fair amount of time for the profiler
self.ut = t # to stop the stopwatch (from the time it receives an event).
# Similarly, there is a delay from the time that the profiler
# re-starts the stopwatch before the user's code really gets to
# continue. The following code tries to measure the difference on
# a per-event basis. The result can the be placed in the
# Profile.dispatch_event() routine for the given platform. Note
# that this difference is only significant if there are a lot of
# events, and relatively little user code per event. For example,
# code with small functions will typically benefit from having the
# profiler calibrated for the current platform. This *could* be
# done on the fly during init() time, but it is not worth the
# effort. Also note that if too large a value specified, then
# execution time on some functions will actually appear as a
# negative number. It is *normal* for some functions (with very
# low call counts) to have such negative stats, even if the
# calibration figure is "correct."
#
# One alternative to profile-time calibration adjustments (i.e.,
# adding in the magic little delta during each event) is to track
# more carefully the number of events (and cumulatively, the number
# of events during sub functions) that are seen. If this were
# done, then the arithmetic could be done after the fact (i.e., at
# display time). Currently, we track only call/return events.
# These values can be deduced by examining the callees and callers
# vectors for each functions. Hence we *can* almost correct the
# internal time figure at print time (note that we currently don't
# track exception event processing counts). Unfortunately, there
# is currently no similar information for cumulative sub-function
# time. It would not be hard to "get all this info" at profiler
# time. Specifically, we would have to extend the tuples to keep
# counts of this in each frame, and then extend the defs of timing
# tuples to include the significant two figures. I'm a bit fearful
# that this additional feature will slow the heavily optimized
# event/time ratio (i.e., the profiler would run slower, fur a very
# low "value added" feature.)
#
# Plugging in the calibration constant doesn't slow down the
# profiler very much, and the accuracy goes way up.
#**************************************************************
def calibrate(self, m):
# Modified by Tim Peters
n = m
s = self.get_time()
while n:
self.simple()
n = n - 1
f = self.get_time()
my_simple = f - s
#print "Simple =", my_simple,
n = m
s = self.get_time()
while n:
self.instrumented()
n = n - 1
f = self.get_time()
my_inst = f - s
# print "Instrumented =", my_inst
avg_cost = (my_inst - my_simple)/m
#print "Delta/call =", avg_cost, "(profiler fixup constant)"
return avg_cost
# simulate a program with no profiler activity
def simple(self):
a = 1
pass
# simulate a program with call/return event processing
def instrumented(self):
a = 1
self.profiler_simulation(a, a, a)
# simulate an event processing activity (from user's perspective)
def profiler_simulation(self, x, y, z):
t = self.timer()
## t = t[0] + t[1]
self.ut = t
class OldProfile(Profile): class OldProfile(Profile):
"""A derived profiler that simulates the old style profile, providing """A derived profiler that simulates the old style profile, providing
errant results on recursive functions. The reason for the usefulness of errant results on recursive functions. The reason for the usefulness of
this profiler is that it runs faster (i.e., less overhead). It still 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* creates all the caller stats, and is quite useful when there is *no*
recursion in the user's code. 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): This code also shows how easy it is to create a modified profiler.
rt, rtt, rct, rfn, rframe, rcur = self.cur """
if rcur and not rframe is frame:
return self.trace_dispatch_return(rframe, t)
return 0
def trace_dispatch_call(self, frame, t): def trace_dispatch_exception(self, frame, t):
fn = `frame.f_code` rt, rtt, rct, rfn, rframe, rcur = self.cur
if rcur and not rframe is frame:
self.cur = (t, 0, 0, fn, frame, self.cur) return self.trace_dispatch_return(rframe, t)
if self.timings.has_key(fn): return 0
tt, ct, callers = self.timings[fn]
self.timings[fn] = tt, ct, callers
else:
self.timings[fn] = 0, 0, {}
return 1
def trace_dispatch_return(self, frame, t): def trace_dispatch_call(self, frame, t):
rt, rtt, rct, rfn, frame, rcur = self.cur fn = `frame.f_code`
rtt = rtt + t
sft = rtt + rct
pt, ptt, pct, pfn, pframe, pcur = rcur self.cur = (t, 0, 0, fn, frame, self.cur)
self.cur = pt, ptt+rt, pct+sft, pfn, pframe, pcur if self.timings.has_key(fn):
tt, ct, callers = self.timings[fn]
self.timings[fn] = tt, ct, callers
else:
self.timings[fn] = 0, 0, {}
return 1
tt, ct, callers = self.timings[rfn] def trace_dispatch_return(self, frame, t):
if callers.has_key(pfn): rt, rtt, rct, rfn, frame, rcur = self.cur
callers[pfn] = callers[pfn] + 1 rtt = rtt + t
else: sft = rtt + rct
callers[pfn] = 1
self.timings[rfn] = tt+rtt, ct + sft, callers
return 1 pt, ptt, pct, pfn, pframe, pcur = rcur
self.cur = pt, ptt+rt, pct+sft, pfn, pframe, pcur
tt, ct, callers = self.timings[rfn]
if callers.has_key(pfn):
callers[pfn] = callers[pfn] + 1
else:
callers[pfn] = 1
self.timings[rfn] = tt+rtt, ct + sft, callers
return 1
def snapshot_stats(self): def snapshot_stats(self):
self.stats = {} self.stats = {}
for func in self.timings.keys(): for func in self.timings.keys():
tt, ct, callers = self.timings[func] tt, ct, callers = self.timings[func]
callers = callers.copy() callers = callers.copy()
nc = 0 nc = 0
for func_caller in callers.keys(): for func_caller in callers.keys():
nc = nc + callers[func_caller] nc = nc + callers[func_caller]
self.stats[func] = nc, nc, tt, ct, callers self.stats[func] = nc, nc, tt, ct, callers
class HotProfile(Profile): class HotProfile(Profile):
"""The fastest derived profile example. It does not calculate """The fastest derived profile example. It does not calculate
caller-callee relationships, and does not calculate cumulative caller-callee relationships, and does not calculate cumulative
time under a function. It only calculates time spent in a time under a function. It only calculates time spent in a
function, so it runs very quickly due to its very low overhead. 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:
return self.trace_dispatch_return(rframe, t) return self.trace_dispatch_return(rframe, t)
return 0 return 0
def trace_dispatch_call(self, frame, t): def trace_dispatch_call(self, frame, t):
self.cur = (t, 0, frame, self.cur) self.cur = (t, 0, frame, self.cur)
return 1 return 1
def trace_dispatch_return(self, frame, t): def trace_dispatch_return(self, frame, t):
rt, rtt, frame, rcur = self.cur rt, rtt, frame, rcur = self.cur
rfn = `frame.f_code` rfn = `frame.f_code`
pt, ptt, pframe, pcur = rcur pt, ptt, pframe, pcur = rcur
self.cur = pt, ptt+rt, pframe, pcur self.cur = pt, ptt+rt, pframe, pcur
if self.timings.has_key(rfn): if self.timings.has_key(rfn):
nc, tt = self.timings[rfn] nc, tt = self.timings[rfn]
self.timings[rfn] = nc + 1, rt + rtt + tt self.timings[rfn] = nc + 1, rt + rtt + tt
else: else:
self.timings[rfn] = 1, rt + rtt self.timings[rfn] = 1, rt + rtt
return 1 return 1
def snapshot_stats(self): def snapshot_stats(self):
self.stats = {} self.stats = {}
for func in self.timings.keys(): for func in self.timings.keys():
nc, tt = self.timings[func] nc, tt = self.timings[func]
self.stats[func] = nc, nc, tt, 0, {} self.stats[func] = nc, nc, tt, 0, {}
#**************************************************************************** #****************************************************************************
def Stats(*args): def Stats(*args):
print 'Report generating functions are in the "pstats" module\a' print 'Report generating functions are in the "pstats" module\a'
# When invoked as main program, invoke the profiler on a script # When invoked as main program, invoke the profiler on a script
if __name__ == '__main__': if __name__ == '__main__':
import sys import sys
import os import os
if not sys.argv[1:]: if not sys.argv[1:]:
print "usage: profile.py scriptfile [arg] ..." print "usage: profile.py scriptfile [arg] ..."
sys.exit(2) sys.exit(2)
filename = sys.argv[1] # Get script filename filename = sys.argv[1] # Get script filename
del sys.argv[0] # Hide "profile.py" from argument list del sys.argv[0] # Hide "profile.py" from argument list
# Insert script directory in front of module search path # Insert script directory in front of module search path
sys.path.insert(0, os.path.dirname(filename)) sys.path.insert(0, os.path.dirname(filename))
run('execfile(' + `filename` + ')') run('execfile(' + `filename` + ')')

View file

@ -9,7 +9,7 @@
# Copyright 1994, by InfoSeek Corporation, all rights reserved. # Copyright 1994, by InfoSeek Corporation, all rights reserved.
# Written by James Roskind # Written by James Roskind
# #
# Permission to use, copy, modify, and distribute this Python software # Permission to use, copy, modify, and distribute this Python software
# and its associated documentation for any purpose (subject to the # and its associated documentation for any purpose (subject to the
# restriction in the following sentence) without fee is hereby granted, # restriction in the following sentence) without fee is hereby granted,
@ -22,7 +22,7 @@
# to remain in Python, compiled Python, or other languages (such as C) # to remain in Python, compiled Python, or other languages (such as C)
# wherein the modified or derived code is exclusively imported into a # wherein the modified or derived code is exclusively imported into a
# Python module. # Python module.
# #
# INFOSEEK CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS # INFOSEEK CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND # SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS. IN NO EVENT SHALL INFOSEEK CORPORATION BE LIABLE FOR ANY # FITNESS. IN NO EVENT SHALL INFOSEEK CORPORATION BE LIABLE FOR ANY
@ -41,486 +41,485 @@ import re
import fpformat import fpformat
class Stats: class Stats:
"""This class is used for creating reports from data generated by the """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 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 by direct access to members of Profile class, or by reading in a dictionary
that was emitted (via marshal) from the Profile class. that was emitted (via marshal) from the Profile class.
The big change from the previous Profiler (in terms of raw functionality) The big change from the previous Profiler (in terms of raw functionality)
is that an "add()" method has been provided to combine Stats from is that an "add()" method has been provided to combine Stats from
several distinct profile runs. Both the constructor and the add() several distinct profile runs. Both the constructor and the add()
method now take arbitrarily many file names as arguments. method now take arbitrarily many file names as arguments.
All the print methods now take an argument that indicates how many lines All the print methods now take an argument that indicates how many lines
to print. If the arg is a floating point number between 0 and 1.0, then 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 available lines to be printed it is taken as a decimal percentage of the available lines to be printed
(e.g., .1 means print 10% of all available lines). If it is an integer, (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 it is taken to mean the number of lines of data that you wish to have
printed. printed.
The sort_stats() method now processes some additional options (i.e., in The sort_stats() method now processes some additional options (i.e., in
addition to the old -1, 0, 1, or 2). It takes an arbitrary number of quoted 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') 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 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() key of 'the name of the function'. Look at the two tables in sort_stats()
and get_sort_arg_defs(self) for more examples. and get_sort_arg_defs(self) for more examples.
All methods now return "self", so you can string together commands like: All methods now return "self", so you can string together commands like:
Stats('foo', 'goo').strip_dirs().sort_stats('calls').\ Stats('foo', 'goo').strip_dirs().sort_stats('calls').\
print_stats(5).print_callers(5) 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
else: else:
arg = args[0] arg = args[0]
args = args[1:] args = args[1:]
self.init(arg) self.init(arg)
apply(self.add, args).ignore() apply(self.add, args).ignore()
def init(self, arg): def init(self, arg):
self.all_callees = None # calc only if needed self.all_callees = None # calc only if needed
self.files = [] self.files = []
self.fcn_list = None self.fcn_list = None
self.total_tt = 0 self.total_tt = 0
self.total_calls = 0 self.total_calls = 0
self.prim_calls = 0 self.prim_calls = 0
self.max_name_len = 0 self.max_name_len = 0
self.top_level = {} self.top_level = {}
self.stats = {} self.stats = {}
self.sort_arg_dict = {} self.sort_arg_dict = {}
self.load_stats(arg) self.load_stats(arg)
trouble = 1 trouble = 1
try: try:
self.get_top_level_stats() self.get_top_level_stats()
trouble = 0 trouble = 0
finally: finally:
if trouble: if trouble:
print "Invalid timing data", print "Invalid timing data",
if self.files: print self.files[-1], if self.files: print self.files[-1],
print print
def load_stats(self, arg): def load_stats(self, arg):
if not arg: self.stats = {} if not arg: self.stats = {}
elif type(arg) == type(""): elif type(arg) == type(""):
f = open(arg, 'rb') f = open(arg, 'rb')
self.stats = marshal.load(f) self.stats = marshal.load(f)
f.close() f.close()
try: try:
file_stats = os.stat(arg) file_stats = os.stat(arg)
arg = time.ctime(file_stats[8]) + " " + arg arg = time.ctime(file_stats[8]) + " " + arg
except: # in case this is not unix except: # in case this is not unix
pass pass
self.files = [ arg ] self.files = [ arg ]
elif hasattr(arg, 'create_stats'): elif hasattr(arg, 'create_stats'):
arg.create_stats() arg.create_stats()
self.stats = arg.stats self.stats = arg.stats
arg.stats = {} arg.stats = {}
if not self.stats: if not self.stats:
raise TypeError, "Cannot create or construct a " \ raise TypeError, "Cannot create or construct a " \
+ `self.__class__` \ + `self.__class__` \
+ " object from '" + `arg` + "'" + " object from '" + `arg` + "'"
return return
def get_top_level_stats(self): def get_top_level_stats(self):
for func in self.stats.keys(): for func in self.stats.keys():
cc, nc, tt, ct, callers = self.stats[func] cc, nc, tt, ct, callers = self.stats[func]
self.total_calls = self.total_calls + nc self.total_calls = self.total_calls + nc
self.prim_calls = self.prim_calls + cc self.prim_calls = self.prim_calls + cc
self.total_tt = self.total_tt + tt self.total_tt = self.total_tt + tt
if callers.has_key(("jprofile", 0, "profiler")): if callers.has_key(("jprofile", 0, "profiler")):
self.top_level[func] = None self.top_level[func] = None
if len(func_std_string(func)) > self.max_name_len: if len(func_std_string(func)) > self.max_name_len:
self.max_name_len = len(func_std_string(func)) self.max_name_len = len(func_std_string(func))
def add(self, *arg_list):
if not arg_list: return self
if len(arg_list) > 1: apply(self.add, arg_list[1:])
other = arg_list[0]
if type(self) != type(other) or \
self.__class__ != other.__class__:
other = Stats(other)
self.files = self.files + other.files
self.total_calls = self.total_calls + other.total_calls
self.prim_calls = self.prim_calls + other.prim_calls
self.total_tt = self.total_tt + other.total_tt
for func in other.top_level.keys():
self.top_level[func] = None
if self.max_name_len < other.max_name_len: def add(self, *arg_list):
self.max_name_len = other.max_name_len if not arg_list: return self
if len(arg_list) > 1: apply(self.add, arg_list[1:])
other = arg_list[0]
if type(self) != type(other) or \
self.__class__ != other.__class__:
other = Stats(other)
self.files = self.files + other.files
self.total_calls = self.total_calls + other.total_calls
self.prim_calls = self.prim_calls + other.prim_calls
self.total_tt = self.total_tt + other.total_tt
for func in other.top_level.keys():
self.top_level[func] = None
self.fcn_list = None if self.max_name_len < other.max_name_len:
self.max_name_len = other.max_name_len
for func in other.stats.keys(): self.fcn_list = None
if self.stats.has_key(func):
old_func_stat = self.stats[func]
else:
old_func_stat = (0, 0, 0, 0, {},)
self.stats[func] = add_func_stats(old_func_stat, \
other.stats[func])
return self
for func in other.stats.keys():
# list the tuple indices and directions for sorting, if self.stats.has_key(func):
# along with some printable description old_func_stat = self.stats[func]
sort_arg_dict_default = {\ else:
"calls" : (((1,-1), ), "call count"),\ old_func_stat = (0, 0, 0, 0, {},)
"cumulative": (((3,-1), ), "cumulative time"),\ self.stats[func] = add_func_stats(old_func_stat, \
"file" : (((4, 1), ), "file name"),\ other.stats[func])
"line" : (((5, 1), ), "line number"),\ return self
"module" : (((4, 1), ), "file name"),\
"name" : (((6, 1), ), "function name"),\
"nfl" : (((6, 1),(4, 1),(5, 1),), "name/file/line"), \
"pcalls" : (((0,-1), ), "call count"),\
"stdname" : (((7, 1), ), "standard name"),\
"time" : (((2,-1), ), "internal time"),\
}
def get_sort_arg_defs(self):
"""Expand all abbreviations that are unique."""
if not self.sort_arg_dict:
self.sort_arg_dict = dict = {}
std_list = dict.keys()
bad_list = {}
for word in self.sort_arg_dict_default.keys():
fragment = word
while fragment:
if not fragment:
break
if dict.has_key(fragment):
bad_list[fragment] = 0
break
dict[fragment] = self. \
sort_arg_dict_default[word]
fragment = fragment[:-1]
for word in bad_list.keys():
del dict[word]
return self.sort_arg_dict
def sort_stats(self, *field):
if not field:
self.fcn_list = 0
return self
if len(field) == 1 and type(field[0]) == type(1):
# Be compatible with old profiler
field = [ {-1: "stdname", \
0:"calls", \
1:"time", \
2: "cumulative" } [ field[0] ] ]
sort_arg_defs = self.get_sort_arg_defs()
sort_tuple = ()
self.sort_type = ""
connector = ""
for word in field:
sort_tuple = sort_tuple + sort_arg_defs[word][0]
self.sort_type = self.sort_type + connector + \
sort_arg_defs[word][1]
connector = ", "
stats_list = []
for func in self.stats.keys():
cc, nc, tt, ct, callers = self.stats[func]
stats_list.append((cc, nc, tt, ct) + func_split(func) \
+ (func_std_string(func), func,) )
stats_list.sort(TupleComp(sort_tuple).compare)
self.fcn_list = fcn_list = []
for tuple in stats_list:
fcn_list.append(tuple[-1])
return self
def reverse_order(self):
if self.fcn_list: self.fcn_list.reverse()
return self
def strip_dirs(self):
oldstats = self.stats
self.stats = newstats = {}
max_name_len = 0
for func in oldstats.keys():
cc, nc, tt, ct, callers = oldstats[func]
newfunc = func_strip_path(func)
if len(func_std_string(newfunc)) > max_name_len:
max_name_len = len(func_std_string(newfunc))
newcallers = {}
for func2 in callers.keys():
newcallers[func_strip_path(func2)] = \
callers[func2]
if newstats.has_key(newfunc):
newstats[newfunc] = add_func_stats( \
newstats[newfunc],\
(cc, nc, tt, ct, newcallers))
else:
newstats[newfunc] = (cc, nc, tt, ct, newcallers)
old_top = self.top_level
self.top_level = new_top = {}
for func in old_top.keys():
new_top[func_strip_path(func)] = None
self.max_name_len = max_name_len
self.fcn_list = None
self.all_callees = None
return self
def calc_callees(self): # list the tuple indices and directions for sorting,
if self.all_callees: return # along with some printable description
self.all_callees = all_callees = {} sort_arg_dict_default = {\
for func in self.stats.keys(): "calls" : (((1,-1), ), "call count"),\
if not all_callees.has_key(func): "cumulative": (((3,-1), ), "cumulative time"),\
all_callees[func] = {} "file" : (((4, 1), ), "file name"),\
cc, nc, tt, ct, callers = self.stats[func] "line" : (((5, 1), ), "line number"),\
for func2 in callers.keys(): "module" : (((4, 1), ), "file name"),\
if not all_callees.has_key(func2): "name" : (((6, 1), ), "function name"),\
all_callees[func2] = {} "nfl" : (((6, 1),(4, 1),(5, 1),), "name/file/line"), \
all_callees[func2][func] = callers[func2] "pcalls" : (((0,-1), ), "call count"),\
return "stdname" : (((7, 1), ), "standard name"),\
"time" : (((2,-1), ), "internal time"),\
}
#****************************************************************** def get_sort_arg_defs(self):
# The following functions support actual printing of reports """Expand all abbreviations that are unique."""
#****************************************************************** if not self.sort_arg_dict:
self.sort_arg_dict = dict = {}
std_list = dict.keys()
bad_list = {}
for word in self.sort_arg_dict_default.keys():
fragment = word
while fragment:
if not fragment:
break
if dict.has_key(fragment):
bad_list[fragment] = 0
break
dict[fragment] = self. \
sort_arg_dict_default[word]
fragment = fragment[:-1]
for word in bad_list.keys():
del dict[word]
return self.sort_arg_dict
# Optional "amount" is either a line count, or a percentage of lines.
def eval_print_amount(self, sel, list, msg): def sort_stats(self, *field):
new_list = list if not field:
if type(sel) == type(""): self.fcn_list = 0
new_list = [] return self
for func in list: if len(field) == 1 and type(field[0]) == type(1):
if re.search(sel, func_std_string(func)): # Be compatible with old profiler
new_list.append(func) field = [ {-1: "stdname", \
else: 0:"calls", \
count = len(list) 1:"time", \
if type(sel) == type(1.0) and 0.0 <= sel < 1.0: 2: "cumulative" } [ field[0] ] ]
count = int (count * sel + .5)
new_list = list[:count] sort_arg_defs = self.get_sort_arg_defs()
elif type(sel) == type(1) and 0 <= sel < count: sort_tuple = ()
count = sel self.sort_type = ""
new_list = list[:count] connector = ""
if len(list) != len(new_list): for word in field:
msg = msg + " List reduced from " + `len(list)` \ sort_tuple = sort_tuple + sort_arg_defs[word][0]
+ " to " + `len(new_list)` + \ self.sort_type = self.sort_type + connector + \
" due to restriction <" + `sel` + ">\n" sort_arg_defs[word][1]
connector = ", "
return new_list, msg
stats_list = []
for func in self.stats.keys():
cc, nc, tt, ct, callers = self.stats[func]
stats_list.append((cc, nc, tt, ct) + func_split(func) \
+ (func_std_string(func), func,) )
stats_list.sort(TupleComp(sort_tuple).compare)
self.fcn_list = fcn_list = []
for tuple in stats_list:
fcn_list.append(tuple[-1])
return self
def reverse_order(self):
if self.fcn_list: self.fcn_list.reverse()
return self
def strip_dirs(self):
oldstats = self.stats
self.stats = newstats = {}
max_name_len = 0
for func in oldstats.keys():
cc, nc, tt, ct, callers = oldstats[func]
newfunc = func_strip_path(func)
if len(func_std_string(newfunc)) > max_name_len:
max_name_len = len(func_std_string(newfunc))
newcallers = {}
for func2 in callers.keys():
newcallers[func_strip_path(func2)] = \
callers[func2]
if newstats.has_key(newfunc):
newstats[newfunc] = add_func_stats( \
newstats[newfunc],\
(cc, nc, tt, ct, newcallers))
else:
newstats[newfunc] = (cc, nc, tt, ct, newcallers)
old_top = self.top_level
self.top_level = new_top = {}
for func in old_top.keys():
new_top[func_strip_path(func)] = None
self.max_name_len = max_name_len
self.fcn_list = None
self.all_callees = None
return self
def get_print_list(self, sel_list): def calc_callees(self):
width = self.max_name_len if self.all_callees: return
if self.fcn_list: self.all_callees = all_callees = {}
list = self.fcn_list[:] for func in self.stats.keys():
msg = " Ordered by: " + self.sort_type + '\n' if not all_callees.has_key(func):
else: all_callees[func] = {}
list = self.stats.keys() cc, nc, tt, ct, callers = self.stats[func]
msg = " Random listing order was used\n" for func2 in callers.keys():
if not all_callees.has_key(func2):
all_callees[func2] = {}
all_callees[func2][func] = callers[func2]
return
for selection in sel_list: #******************************************************************
list,msg = self.eval_print_amount(selection, list, msg) # The following functions support actual printing of reports
#******************************************************************
count = len(list) # Optional "amount" is either a line count, or a percentage of lines.
if not list: def eval_print_amount(self, sel, list, msg):
return 0, list new_list = list
print msg if type(sel) == type(""):
if count < len(self.stats): new_list = []
width = 0 for func in list:
for func in list: if re.search(sel, func_std_string(func)):
if len(func_std_string(func)) > width: new_list.append(func)
width = len(func_std_string(func)) else:
return width+2, list count = len(list)
if type(sel) == type(1.0) and 0.0 <= sel < 1.0:
def print_stats(self, *amount): count = int (count * sel + .5)
for filename in self.files: new_list = list[:count]
print filename elif type(sel) == type(1) and 0 <= sel < count:
if self.files: print count = sel
indent = " " new_list = list[:count]
for func in self.top_level.keys(): if len(list) != len(new_list):
print indent, func_get_function_name(func) msg = msg + " List reduced from " + `len(list)` \
+ " to " + `len(new_list)` + \
print indent, self.total_calls, "function calls", " due to restriction <" + `sel` + ">\n"
if self.total_calls != self.prim_calls:
print "(" + `self.prim_calls`, "primitive calls)",
print "in", fpformat.fix(self.total_tt, 3), "CPU seconds"
print
width, list = self.get_print_list(amount)
if list:
self.print_title()
for func in list:
self.print_line(func)
print
print
return self
return new_list, msg
def print_callees(self, *amount):
width, list = self.get_print_list(amount)
if list:
self.calc_callees()
self.print_call_heading(width, "called...")
for func in list:
if self.all_callees.has_key(func):
self.print_call_line(width, \
func, self.all_callees[func])
else:
self.print_call_line(width, func, {})
print
print
return self
def print_callers(self, *amount):
width, list = self.get_print_list(amount)
if list:
self.print_call_heading(width, "was called by...")
for func in list:
cc, nc, tt, ct, callers = self.stats[func]
self.print_call_line(width, func, callers)
print
print
return self
def print_call_heading(self, name_size, column_title):
print string.ljust("Function ", name_size) + column_title
def print_call_line(self, name_size, source, call_dict):
print string.ljust(func_std_string(source), name_size),
if not call_dict:
print "--"
return
clist = call_dict.keys()
clist.sort()
name_size = name_size + 1
indent = ""
for func in clist:
name = func_std_string(func)
print indent*name_size + name + '(' \
+ `call_dict[func]`+')', \
f8(self.stats[func][3])
indent = " "
def print_title(self): def get_print_list(self, sel_list):
print string.rjust('ncalls', 9), width = self.max_name_len
print string.rjust('tottime', 8), if self.fcn_list:
print string.rjust('percall', 8), list = self.fcn_list[:]
print string.rjust('cumtime', 8), msg = " Ordered by: " + self.sort_type + '\n'
print string.rjust('percall', 8), else:
print 'filename:lineno(function)' list = self.stats.keys()
msg = " Random listing order was used\n"
for selection in sel_list:
list,msg = self.eval_print_amount(selection, list, msg)
count = len(list)
if not list:
return 0, list
print msg
if count < len(self.stats):
width = 0
for func in list:
if len(func_std_string(func)) > width:
width = len(func_std_string(func))
return width+2, list
def print_stats(self, *amount):
for filename in self.files:
print filename
if self.files: print
indent = " "
for func in self.top_level.keys():
print indent, func_get_function_name(func)
print indent, self.total_calls, "function calls",
if self.total_calls != self.prim_calls:
print "(" + `self.prim_calls`, "primitive calls)",
print "in", fpformat.fix(self.total_tt, 3), "CPU seconds"
print
width, list = self.get_print_list(amount)
if list:
self.print_title()
for func in list:
self.print_line(func)
print
print
return self
def print_line(self, func): # hack : should print percentages def print_callees(self, *amount):
cc, nc, tt, ct, callers = self.stats[func] width, list = self.get_print_list(amount)
c = `nc` if list:
if nc != cc: self.calc_callees()
c = c + '/' + `cc`
print string.rjust(c, 9), self.print_call_heading(width, "called...")
print f8(tt), for func in list:
if nc == 0: if self.all_callees.has_key(func):
print ' '*8, self.print_call_line(width, \
else: func, self.all_callees[func])
print f8(tt/nc), else:
print f8(ct), self.print_call_line(width, func, {})
if cc == 0: print
print ' '*8, print
else: return self
print f8(ct/cc),
print func_std_string(func) def print_callers(self, *amount):
width, list = self.get_print_list(amount)
if list:
self.print_call_heading(width, "was called by...")
for func in list:
cc, nc, tt, ct, callers = self.stats[func]
self.print_call_line(width, func, callers)
print
print
return self
def print_call_heading(self, name_size, column_title):
print string.ljust("Function ", name_size) + column_title
def ignore(self): def print_call_line(self, name_size, source, call_dict):
pass # has no return value, so use at end of line :-) print string.ljust(func_std_string(source), name_size),
if not call_dict:
print "--"
return
clist = call_dict.keys()
clist.sort()
name_size = name_size + 1
indent = ""
for func in clist:
name = func_std_string(func)
print indent*name_size + name + '(' \
+ `call_dict[func]`+')', \
f8(self.stats[func][3])
indent = " "
def print_title(self):
print string.rjust('ncalls', 9),
print string.rjust('tottime', 8),
print string.rjust('percall', 8),
print string.rjust('cumtime', 8),
print string.rjust('percall', 8),
print 'filename:lineno(function)'
def print_line(self, func): # hack : should print percentages
cc, nc, tt, ct, callers = self.stats[func]
c = `nc`
if nc != cc:
c = c + '/' + `cc`
print string.rjust(c, 9),
print f8(tt),
if nc == 0:
print ' '*8,
else:
print f8(tt/nc),
print f8(ct),
if cc == 0:
print ' '*8,
else:
print f8(ct/cc),
print func_std_string(func)
def ignore(self):
pass # has no return value, so use at end of line :-)
class TupleComp: class TupleComp:
"""This class provides a generic function for comparing any two tuples. """This class provides a generic function for comparing any two tuples.
Each instance records a list of tuple-indices (from most significant Each instance records a list of tuple-indices (from most significant
to least significant), and sort direction (ascending or decending) for to least significant), and sort direction (ascending or decending) for
each tuple-index. The compare functions can then be used as the function 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 argument to the system sort() function when a list of tuples need to be
sorted in the instances order.""" 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
def compare (self, left, right):
for index, direction in self.comp_select_list:
l = left[index]
r = right[index]
if l < r:
return -direction
if l > r:
return direction
return 0
def compare (self, left, right):
for index, direction in self.comp_select_list:
l = left[index]
r = right[index]
if l < r:
return -direction
if l > r:
return direction
return 0
#************************************************************************** #**************************************************************************
def func_strip_path(func_name): def func_strip_path(func_name):
file, line, name = func_name file, line, name = func_name
return os.path.basename(file), line, name return os.path.basename(file), line, name
def func_get_function_name(func): def func_get_function_name(func):
return func[2] return func[2]
def func_std_string(func_name): # match what old profile produced def func_std_string(func_name): # match what old profile produced
file, line, name = func_name file, line, name = func_name
return file + ":" + `line` + "(" + name + ")" return file + ":" + `line` + "(" + name + ")"
def func_split(func_name): def func_split(func_name):
return func_name return func_name
#************************************************************************** #**************************************************************************
# The following functions combine statists for pairs functions. # The following functions combine statists for pairs functions.
# The bulk of the processing involves correctly handling "call" lists, # The bulk of the processing involves correctly handling "call" lists,
# such as callers and callees. # such as callers and callees.
#************************************************************************** #**************************************************************************
def add_func_stats(target, source): def add_func_stats(target, source):
"""Add together all the stats for two profile entries.""" """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))
def add_callers(target, source): def add_callers(target, source):
"""Combine two caller lists in a single list.""" """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]
for func in source.keys(): for func in source.keys():
if new_callers.has_key(func): if new_callers.has_key(func):
new_callers[func] = source[func] + new_callers[func] new_callers[func] = source[func] + new_callers[func]
else: else:
new_callers[func] = source[func] new_callers[func] = source[func]
return new_callers return new_callers
def count_calls(callers): def count_calls(callers):
"""Sum the caller statistics to get total number of calls received.""" """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]
return nc return nc
#************************************************************************** #**************************************************************************
# The following functions support printing of reports # The following functions support printing of reports
#************************************************************************** #**************************************************************************
def f8(x): def f8(x):
return string.rjust(fpformat.fix(x, 3), 8) return string.rjust(fpformat.fix(x, 3), 8)

View file

@ -1,9 +1,9 @@
"""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.
# See: W. Richard Stevens. 1992. Advanced Programming in the # See: W. Richard Stevens. 1992. Advanced Programming in the
# UNIX Environment. Chapter 19. # UNIX Environment. Chapter 19.
# Author: Steen Lumholt -- with additions by Guido. # Author: Steen Lumholt -- with additions by Guido.
from select import select from select import select
@ -17,133 +17,133 @@ STDERR_FILENO = 2
CHILD = 0 CHILD = 0
def openpty(): def openpty():
"""openpty() -> (master_fd, slave_fd) """openpty() -> (master_fd, slave_fd)
Open a pty master/slave pair, using os.openpty() if possible.""" Open a pty master/slave pair, using os.openpty() if possible."""
try: try:
return os.openpty() return os.openpty()
except (AttributeError, OSError): except (AttributeError, OSError):
pass pass
master_fd, slave_name = _open_terminal() master_fd, slave_name = _open_terminal()
slave_fd = slave_open(slave_name) slave_fd = slave_open(slave_name)
return master_fd, slave_fd return master_fd, slave_fd
def master_open(): def master_open():
"""master_open() -> (master_fd, slave_name) """master_open() -> (master_fd, slave_name)
Open a pty master and return the fd, and the filename of the slave end. Open a pty master and return the fd, and the filename of the slave end.
Deprecated, use openpty() instead.""" Deprecated, use openpty() instead."""
try: try:
master_fd, slave_fd = os.openpty() master_fd, slave_fd = os.openpty()
except (AttributeError, OSError): except (AttributeError, OSError):
pass pass
else: else:
slave_name = os.ttyname(slave_fd) slave_name = os.ttyname(slave_fd)
os.close(slave_fd) os.close(slave_fd)
return master_fd, slave_name return master_fd, slave_name
return _open_terminal() return _open_terminal()
def _open_terminal(): def _open_terminal():
"""Open pty master and return (master_fd, tty_name). """Open pty master and return (master_fd, tty_name).
SGI and generic BSD version, for when openpty() fails.""" SGI and generic BSD version, for when openpty() fails."""
try: try:
import sgi import sgi
except ImportError: except ImportError:
pass pass
else: else:
try: try:
tty_name, master_fd = sgi._getpty(FCNTL.O_RDWR, 0666, 0) tty_name, master_fd = sgi._getpty(FCNTL.O_RDWR, 0666, 0)
except IOError, msg: except IOError, msg:
raise os.error, msg raise os.error, msg
return master_fd, tty_name return master_fd, tty_name
for x in 'pqrstuvwxyzPQRST': for x in 'pqrstuvwxyzPQRST':
for y in '0123456789abcdef': for y in '0123456789abcdef':
pty_name = '/dev/pty' + x + y pty_name = '/dev/pty' + x + y
try: try:
fd = os.open(pty_name, FCNTL.O_RDWR) fd = os.open(pty_name, FCNTL.O_RDWR)
except os.error: except os.error:
continue continue
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'
def slave_open(tty_name): def slave_open(tty_name):
"""slave_open(tty_name) -> slave_fd """slave_open(tty_name) -> slave_fd
Open the pty slave and acquire the controlling terminal, returning Open the pty slave and acquire the controlling terminal, returning
opened filedescriptor. opened filedescriptor.
Deprecated, use openpty() instead.""" Deprecated, use openpty() instead."""
return os.open(tty_name, FCNTL.O_RDWR) return os.open(tty_name, FCNTL.O_RDWR)
def fork(): def fork():
"""fork() -> (pid, master_fd) """fork() -> (pid, master_fd)
Fork and make the child a session leader with a controlling terminal.""" Fork and make the child a session leader with a controlling terminal."""
try: try:
pid, fd = os.forkpty() pid, fd = os.forkpty()
except (AttributeError, OSError): except (AttributeError, OSError):
pass pass
else: else:
if pid == CHILD: if pid == CHILD:
try: try:
os.setsid() os.setsid()
except OSError: except OSError:
# os.forkpty() already set us session leader # os.forkpty() already set us session leader
pass pass
return pid, fd return pid, fd
master_fd, slave_fd = openpty() master_fd, slave_fd = openpty()
pid = os.fork() pid = os.fork()
if pid == CHILD: if pid == CHILD:
# Establish a new session. # Establish a new session.
os.setsid() os.setsid()
os.close(master_fd) os.close(master_fd)
# Slave becomes stdin/stdout/stderr of child. # Slave becomes stdin/stdout/stderr of child.
os.dup2(slave_fd, STDIN_FILENO) os.dup2(slave_fd, STDIN_FILENO)
os.dup2(slave_fd, STDOUT_FILENO) os.dup2(slave_fd, STDOUT_FILENO)
os.dup2(slave_fd, STDERR_FILENO) os.dup2(slave_fd, STDERR_FILENO)
if (slave_fd > STDERR_FILENO): if (slave_fd > STDERR_FILENO):
os.close (slave_fd) os.close (slave_fd)
# Parent and child process. # Parent and child process.
return pid, master_fd return pid, master_fd
def _writen(fd, data): def _writen(fd, data):
"""Write all the data to a descriptor.""" """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:]
def _read(fd): def _read(fd):
"""Default read function.""" """Default read function."""
return os.read(fd, 1024) return os.read(fd, 1024)
def _copy(master_fd, master_read=_read, stdin_read=_read): def _copy(master_fd, master_read=_read, stdin_read=_read):
"""Parent copy loop. """Parent copy loop.
Copies Copies
pty master -> standard output (master_read) pty master -> standard output (master_read)
standard input -> pty master (stdin_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], [], [])
if master_fd in rfds: if master_fd in rfds:
data = master_read(master_fd) data = master_read(master_fd)
os.write(STDOUT_FILENO, data) os.write(STDOUT_FILENO, data)
if STDIN_FILENO in rfds: if STDIN_FILENO in rfds:
data = stdin_read(STDIN_FILENO) data = stdin_read(STDIN_FILENO)
_writen(master_fd, data) _writen(master_fd, data)
def spawn(argv, master_read=_read, stdin_read=_read): def spawn(argv, master_read=_read, stdin_read=_read):
"""Create a spawned process.""" """Create a spawned process."""
if type(argv) == type(''): if type(argv) == type(''):
argv = (argv,) argv = (argv,)
pid, master_fd = fork() pid, master_fd = fork()
if pid == CHILD: if pid == CHILD:
apply(os.execlp, (argv[0],) + argv) apply(os.execlp, (argv[0],) + argv)
mode = tty.tcgetattr(STDIN_FILENO) mode = tty.tcgetattr(STDIN_FILENO)
tty.setraw(STDIN_FILENO) tty.setraw(STDIN_FILENO)
try: try:
_copy(master_fd, master_read, stdin_read) _copy(master_fd, master_read, stdin_read)
except: except:
tty.tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, mode) tty.tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, mode)

View file

@ -4,7 +4,7 @@ Parse enough of a Python file to recognize class and method
definitions and to find out the superclasses of a class. definitions and to find out the superclasses of a class.
The interface consists of a single function: The interface consists of a single function:
readmodule(module, path) readmodule(module, path)
module is the name of a Python module, path is an optional list of module is the name of a Python module, path is an optional list of
directories where the module is to be searched. If present, path is directories where the module is to be searched. If present, path is
prepended to the system search path sys.path. prepended to the system search path sys.path.
@ -15,11 +15,11 @@ are class instances of the class Class defined here.
A class is described by the class Class in this module. Instances A class is described by the class Class in this module. Instances
of this class have the following instance variables: of this class have the following instance variables:
name -- the name of the class name -- the name of the class
super -- a list of super classes (Class instances) super -- a list of super classes (Class instances)
methods -- a dictionary of methods methods -- a dictionary of methods
file -- the file in which the class was defined file -- the file in which the class was defined
lineno -- the line in the file on which the class statement occurred lineno -- the line in the file on which the class statement occurred
The dictionary of methods uses the method names as keys and the line The dictionary of methods uses the method names as keys and the line
numbers on which the method was defined as values. numbers on which the method was defined as values.
If the name of a super class is not recognized, the corresponding If the name of a super class is not recognized, the corresponding
@ -64,52 +64,52 @@ TABWIDTH = 8
_getnext = re.compile(r""" _getnext = re.compile(r"""
(?P<String> (?P<String>
\""" [^"\\]* (?: \""" [^"\\]* (?:
(?: \\. | "(?!"") ) (?: \\. | "(?!"") )
[^"\\]* [^"\\]*
)* )*
\""" \"""
| ''' [^'\\]* (?: | ''' [^'\\]* (?:
(?: \\. | '(?!'') ) (?: \\. | '(?!'') )
[^'\\]* [^'\\]*
)* )*
''' '''
) )
| (?P<Method> | (?P<Method>
^ ^
(?P<MethodIndent> [ \t]* ) (?P<MethodIndent> [ \t]* )
def [ \t]+ def [ \t]+
(?P<MethodName> [a-zA-Z_] \w* ) (?P<MethodName> [a-zA-Z_] \w* )
[ \t]* \( [ \t]* \(
) )
| (?P<Class> | (?P<Class>
^ ^
(?P<ClassIndent> [ \t]* ) (?P<ClassIndent> [ \t]* )
class [ \t]+ class [ \t]+
(?P<ClassName> [a-zA-Z_] \w* ) (?P<ClassName> [a-zA-Z_] \w* )
[ \t]* [ \t]*
(?P<ClassSupers> \( [^)\n]* \) )? (?P<ClassSupers> \( [^)\n]* \) )?
[ \t]* : [ \t]* :
) )
| (?P<Import> | (?P<Import>
^ import [ \t]+ ^ import [ \t]+
(?P<ImportList> [^#;\n]+ ) (?P<ImportList> [^#;\n]+ )
) )
| (?P<ImportFrom> | (?P<ImportFrom>
^ from [ \t]+ ^ from [ \t]+
(?P<ImportFromPath> (?P<ImportFromPath>
[a-zA-Z_] \w* [a-zA-Z_] \w*
(?: (?:
[ \t]* \. [ \t]* [a-zA-Z_] \w* [ \t]* \. [ \t]* [a-zA-Z_] \w*
)* )*
) )
[ \t]+ [ \t]+
import [ \t]+ import [ \t]+
(?P<ImportFromList> [^#;\n]+ ) (?P<ImportFromList> [^#;\n]+ )
) )
""", re.VERBOSE | re.DOTALL | re.MULTILINE).search """, re.VERBOSE | re.DOTALL | re.MULTILINE).search
@ -117,220 +117,220 @@ _modules = {} # cache of modules we've seen
# each Python class is represented by an instance of this class # each Python class is represented by an instance of this class
class Class: class Class:
'''Class to represent a Python class.''' '''Class to represent a Python class.'''
def __init__(self, module, name, super, file, lineno): def __init__(self, module, name, super, file, lineno):
self.module = module self.module = module
self.name = name self.name = name
if super is None: if super is None:
super = [] super = []
self.super = super self.super = super
self.methods = {} self.methods = {}
self.file = file self.file = file
self.lineno = lineno self.lineno = lineno
def _addmethod(self, name, lineno): def _addmethod(self, name, lineno):
self.methods[name] = lineno self.methods[name] = lineno
class Function(Class): class Function(Class):
'''Class to represent a top-level Python function''' '''Class to represent a top-level Python function'''
def __init__(self, module, name, file, lineno): def __init__(self, module, name, file, lineno):
Class.__init__(self, module, name, None, file, lineno) Class.__init__(self, module, name, None, file, lineno)
def _addmethod(self, name, lineno): def _addmethod(self, name, lineno):
assert 0, "Function._addmethod() shouldn't be called" assert 0, "Function._addmethod() shouldn't be called"
def readmodule(module, path=[], inpackage=0): def readmodule(module, path=[], inpackage=0):
'''Backwards compatible interface. '''Backwards compatible interface.
Like readmodule_ex() but strips Function objects from the Like readmodule_ex() but strips Function objects from the
resulting dictionary.''' resulting dictionary.'''
dict = readmodule_ex(module, path, inpackage) dict = readmodule_ex(module, path, inpackage)
res = {} res = {}
for key, value in dict.items(): for key, value in dict.items():
if not isinstance(value, Function): if not isinstance(value, Function):
res[key] = value res[key] = value
return res return res
def readmodule_ex(module, path=[], inpackage=0): def readmodule_ex(module, path=[], inpackage=0):
'''Read a module file and return a dictionary of classes. '''Read a module file and return a dictionary of classes.
Search for MODULE in PATH and sys.path, read and parse the Search for MODULE in PATH and sys.path, read and parse the
module and return a dictionary with one entry for each class module and return a dictionary with one entry for each class
found in the module.''' found in the module.'''
dict = {} dict = {}
i = string.rfind(module, '.') i = string.rfind(module, '.')
if i >= 0: if i >= 0:
# Dotted module name # Dotted module name
package = string.strip(module[:i]) package = string.strip(module[:i])
submodule = string.strip(module[i+1:]) submodule = string.strip(module[i+1:])
parent = readmodule(package, path, inpackage) parent = readmodule(package, path, inpackage)
child = readmodule(submodule, parent['__path__'], 1) child = readmodule(submodule, parent['__path__'], 1)
return child return child
if _modules.has_key(module): if _modules.has_key(module):
# we've seen this module before... # we've seen this module before...
return _modules[module] return _modules[module]
if module in sys.builtin_module_names: if module in sys.builtin_module_names:
# this is a built-in module # this is a built-in module
_modules[module] = dict _modules[module] = dict
return dict return dict
# search the path for the module # search the path for the module
f = None f = None
if inpackage: if inpackage:
try: try:
f, file, (suff, mode, type) = \ f, file, (suff, mode, type) = \
imp.find_module(module, path) imp.find_module(module, path)
except ImportError: except ImportError:
f = None f = None
if f is None: if f is None:
fullpath = list(path) + sys.path fullpath = list(path) + sys.path
f, file, (suff, mode, type) = imp.find_module(module, fullpath) f, file, (suff, mode, type) = imp.find_module(module, fullpath)
if type == imp.PKG_DIRECTORY: if type == imp.PKG_DIRECTORY:
dict['__path__'] = [file] dict['__path__'] = [file]
_modules[module] = dict _modules[module] = dict
path = [file] + path path = [file] + path
f, file, (suff, mode, type) = \ f, file, (suff, mode, type) = \
imp.find_module('__init__', [file]) imp.find_module('__init__', [file])
if type != imp.PY_SOURCE: if type != imp.PY_SOURCE:
# not Python source, can't do anything with this module # not Python source, can't do anything with this module
f.close() f.close()
_modules[module] = dict _modules[module] = dict
return dict return dict
_modules[module] = dict _modules[module] = dict
imports = [] imports = []
classstack = [] # stack of (class, indent) pairs classstack = [] # stack of (class, indent) pairs
src = f.read() src = f.read()
f.close() f.close()
# To avoid having to stop the regexp at each newline, instead # To avoid having to stop the regexp at each newline, instead
# when we need a line number we simply string.count the number of # when we need a line number we simply string.count the number of
# newlines in the string since the last time we did this; i.e., # newlines in the string since the last time we did this; i.e.,
# lineno = lineno + \ # lineno = lineno + \
# string.count(src, '\n', last_lineno_pos, here) # string.count(src, '\n', last_lineno_pos, here)
# last_lineno_pos = here # last_lineno_pos = here
countnl = string.count countnl = string.count
lineno, last_lineno_pos = 1, 0 lineno, last_lineno_pos = 1, 0
i = 0 i = 0
while 1: while 1:
m = _getnext(src, i) m = _getnext(src, i)
if not m: if not m:
break break
start, i = m.span() start, i = m.span()
if m.start("Method") >= 0: if m.start("Method") >= 0:
# found a method definition or function # found a method definition or function
thisindent = _indent(m.group("MethodIndent")) thisindent = _indent(m.group("MethodIndent"))
meth_name = m.group("MethodName") meth_name = m.group("MethodName")
lineno = lineno + \ lineno = lineno + \
countnl(src, '\n', countnl(src, '\n',
last_lineno_pos, start) last_lineno_pos, start)
last_lineno_pos = start last_lineno_pos = start
# close all classes indented at least as much # close all classes indented at least as much
while classstack and \ while classstack and \
classstack[-1][1] >= thisindent: classstack[-1][1] >= thisindent:
del classstack[-1] del classstack[-1]
if classstack: if classstack:
# it's a class method # it's a class method
cur_class = classstack[-1][0] cur_class = classstack[-1][0]
cur_class._addmethod(meth_name, lineno) cur_class._addmethod(meth_name, lineno)
else: else:
# it's a function # it's a function
f = Function(module, meth_name, f = Function(module, meth_name,
file, lineno) file, lineno)
dict[meth_name] = f dict[meth_name] = f
elif m.start("String") >= 0: elif m.start("String") >= 0:
pass pass
elif m.start("Class") >= 0: elif m.start("Class") >= 0:
# we found a class definition # we found a class definition
thisindent = _indent(m.group("ClassIndent")) thisindent = _indent(m.group("ClassIndent"))
# close all classes indented at least as much # close all classes indented at least as much
while classstack and \ while classstack and \
classstack[-1][1] >= thisindent: classstack[-1][1] >= thisindent:
del classstack[-1] del classstack[-1]
lineno = lineno + \ lineno = lineno + \
countnl(src, '\n', last_lineno_pos, start) countnl(src, '\n', last_lineno_pos, start)
last_lineno_pos = start last_lineno_pos = start
class_name = m.group("ClassName") class_name = m.group("ClassName")
inherit = m.group("ClassSupers") inherit = m.group("ClassSupers")
if inherit: if inherit:
# the class inherits from other classes # the class inherits from other classes
inherit = string.strip(inherit[1:-1]) inherit = string.strip(inherit[1:-1])
names = [] names = []
for n in string.splitfields(inherit, ','): for n in string.splitfields(inherit, ','):
n = string.strip(n) n = string.strip(n)
if dict.has_key(n): if dict.has_key(n):
# we know this super class # we know this super class
n = dict[n] n = dict[n]
else: else:
c = string.splitfields(n, '.') c = string.splitfields(n, '.')
if len(c) > 1: if len(c) > 1:
# super class # super class
# is of the # is of the
# form module.class: # form module.class:
# look in # look in
# module for class # module for class
m = c[-2] m = c[-2]
c = c[-1] c = c[-1]
if _modules.has_key(m): if _modules.has_key(m):
d = _modules[m] d = _modules[m]
if d.has_key(c): if d.has_key(c):
n = d[c] n = d[c]
names.append(n) names.append(n)
inherit = names inherit = names
# remember this class # remember this class
cur_class = Class(module, class_name, inherit, cur_class = Class(module, class_name, inherit,
file, lineno) file, lineno)
dict[class_name] = cur_class dict[class_name] = cur_class
classstack.append((cur_class, thisindent)) classstack.append((cur_class, thisindent))
elif m.start("Import") >= 0: elif m.start("Import") >= 0:
# import module # import module
for n in string.split(m.group("ImportList"), ','): for n in string.split(m.group("ImportList"), ','):
n = string.strip(n) n = string.strip(n)
try: try:
# recursively read the imported module # recursively read the imported module
d = readmodule(n, path, inpackage) d = readmodule(n, path, inpackage)
except: except:
##print 'module', n, 'not found' ##print 'module', n, 'not found'
pass pass
elif m.start("ImportFrom") >= 0: elif m.start("ImportFrom") >= 0:
# from module import stuff # from module import stuff
mod = m.group("ImportFromPath") mod = m.group("ImportFromPath")
names = string.split(m.group("ImportFromList"), ',') names = string.split(m.group("ImportFromList"), ',')
try: try:
# recursively read the imported module # recursively read the imported module
d = readmodule(mod, path, inpackage) d = readmodule(mod, path, inpackage)
except: except:
##print 'module', mod, 'not found' ##print 'module', mod, 'not found'
continue continue
# add any classes that were defined in the # add any classes that were defined in the
# imported module to our name space if they # imported module to our name space if they
# were mentioned in the list # were mentioned in the list
for n in names: for n in names:
n = string.strip(n) n = string.strip(n)
if d.has_key(n): if d.has_key(n):
dict[n] = d[n] dict[n] = d[n]
elif n == '*': elif n == '*':
# only add a name if not # only add a name if not
# already there (to mimic what # already there (to mimic what
# Python does internally) # Python does internally)
# also don't add names that # also don't add names that
# start with _ # start with _
for n in d.keys(): for n in d.keys():
if n[0] != '_' and \ if n[0] != '_' and \
not dict.has_key(n): not dict.has_key(n):
dict[n] = d[n] dict[n] = d[n]
else: else:
assert 0, "regexp _getnext found something unexpected" assert 0, "regexp _getnext found something unexpected"
return dict return dict
def _indent(ws, _expandtabs=string.expandtabs): def _indent(ws, _expandtabs=string.expandtabs):
return len(_expandtabs(ws, TABWIDTH)) return len(_expandtabs(ws, TABWIDTH))

View file

@ -26,7 +26,7 @@ def encode(input, output, quotetabs):
'input' and 'output' are files with readline() and write() methods. 'input' and 'output' are files with readline() and write() methods.
The 'quotetabs' flag indicates whether tabs should be quoted. The 'quotetabs' flag indicates whether tabs should be quoted.
""" """
while 1: while 1:
line = input.readline() line = input.readline()
if not line: if not line: