P
Peter Kleiweg
I'm still new to Python. All my experience with OO programming
is in a distant past with C++. Now I have written my first class
in Python. The class behaves exactly as I want, but I would like
to get comments about coding style. I'm especially unsure about
how a class should be documented, what to put in, and where.
When to use double quotes, and when single. For instance, the
doc string at the top must be in double quotes, or else the
pydoc search engine won't find the description. Any
recommendation for other mark-up or meta-data I should include?
I won't tell you what the class is about, because that should be
clear from code documentation. If not, I have work to do. Here
it is:
# -*- coding: iso-8859-1 -*-
"""
Handling of arguments: options, arguments, file(s) content iterator
For small scripts that:
- read some command line options
- read some command line positional arguments
- iterate over all lines of some files given on the command line, or stdin if none given
- give usage message if positional arguments are missing
- give usage message if input files are missing and stdin is not redirected
"""
__author__ = 'Peter Kleiweg'
__version__ = '0.1'
__date__ = '2004/08/27'
import os, sys, getopt
class Args:
"""
Instance data:
progname (string) -- name of program
opt (dictionary) -- options with values
infile (string) -- name of current file being processed
lineno (int) -- line number of last line read in current file
linesum (int) -- total of lines read
"""
def __init__(self, usage='Usage: %(progname)s [opt...] [file...]') :
"init, usage string: embed program name as %(progname)s"
self.progname = os.path.basename(sys.argv[0])
self.opt = {}
self.infile = None
self.lineno = 0
self.linesum = 0
self._argv = sys.argv[1:]
self._argc = len(self._argv)
self._usage = usage % {'progname': self.progname}
def __iter__(self):
"iterator set-up"
if self._argc == 0 and sys.stdin.isatty():
self.usage()
if self._argc == 0:
self.infile = '<stdin>'
self._stdin = 1
self._in = sys.stdin
else:
self.infile = self._argv.pop(0)
self._argc -= 1
self._stdin = 0
self._in = open(self.infile, 'r')
return self
def next(self):
"iterator next"
line = self._in.readline()
if line:
self.lineno += 1
self.linesum += 1
return line
self.lineno = -1
self.infile = None
if self._stdin:
raise StopIteration
self._in.close()
if self._argc < 1:
raise StopIteration
self.lineno = 0
self.infile = self._argv.pop(0)
self._argc -= 1
self._in = open(self.infile, 'r')
return self.next()
def warning(self, text):
"print warning message to stderr, possibly with filename and lineno"
if self.lineno > 0:
print >> sys.stderr, '%s:%i: warning: %s' % (self.infile, self.lineno, text)
else:
print >> sys.stderr, '\nWarning %s: %s\n' % (self.progname, text)
def error(self, text):
"print error message to stderr, possibly with filename and lineno, and exit"
if self.lineno > 0:
print >> sys.stderr, '%s:%i: %s' % (self.infile, self.lineno, text)
else:
print >> sys.stderr, '\nError %s: %s\n' % (self.progname, text)
sys.exit(1)
def usage(self):
"print usage message"
print >> sys.stderr, '\n' + self._usage + '\n'
sys.exit(1)
def shift(self):
"pop first of remaining arguments (shift)"
if self._argc < 1:
self.usage()
self._argc -= 1
return self._argv.pop(0)
def pop(self):
"pop last of remaining arguments"
if self._argc < 1:
self.usage()
self._argc -= 1
return self._argv.pop()
def getopt(self, shortopts, longopts=[]):
"get options and merge into dict 'opt'"
options, self._argv = getopt.getopt(self._argv, shortopts, longopts)
self.opt.update(dict(options))
self._argc = len(self._argv)
if __name__ == '__main__':
a = Args('Usage: %(progname)s [-a value] [-b value] [-c] word [file...]')
a.opt['-a'] = 'option a' # set some default option values
a.opt['-b'] = 'option b' #
a.getopt('a:b:c') # get user supplied option values
word = a.shift() # get the first of the remaining arguments
# use a.pop() to get the last instead
for line in a: # iterate over the contents of all remaining arguments (filenames)
if a.lineno == 1:
print 'starting new file:', a.infile
a.warning(line.rstrip())
print 'Options:', a.opt
print 'Word:', word
print 'Total number of lines:', a.linesum
print sys.argv # unchanged
a.warning('warn 1') # print a warning
a.error('error') # print an error message and exit
a.warning('warn 2') # this won't show
is in a distant past with C++. Now I have written my first class
in Python. The class behaves exactly as I want, but I would like
to get comments about coding style. I'm especially unsure about
how a class should be documented, what to put in, and where.
When to use double quotes, and when single. For instance, the
doc string at the top must be in double quotes, or else the
pydoc search engine won't find the description. Any
recommendation for other mark-up or meta-data I should include?
I won't tell you what the class is about, because that should be
clear from code documentation. If not, I have work to do. Here
it is:
# -*- coding: iso-8859-1 -*-
"""
Handling of arguments: options, arguments, file(s) content iterator
For small scripts that:
- read some command line options
- read some command line positional arguments
- iterate over all lines of some files given on the command line, or stdin if none given
- give usage message if positional arguments are missing
- give usage message if input files are missing and stdin is not redirected
"""
__author__ = 'Peter Kleiweg'
__version__ = '0.1'
__date__ = '2004/08/27'
import os, sys, getopt
class Args:
"""
Instance data:
progname (string) -- name of program
opt (dictionary) -- options with values
infile (string) -- name of current file being processed
lineno (int) -- line number of last line read in current file
linesum (int) -- total of lines read
"""
def __init__(self, usage='Usage: %(progname)s [opt...] [file...]') :
"init, usage string: embed program name as %(progname)s"
self.progname = os.path.basename(sys.argv[0])
self.opt = {}
self.infile = None
self.lineno = 0
self.linesum = 0
self._argv = sys.argv[1:]
self._argc = len(self._argv)
self._usage = usage % {'progname': self.progname}
def __iter__(self):
"iterator set-up"
if self._argc == 0 and sys.stdin.isatty():
self.usage()
if self._argc == 0:
self.infile = '<stdin>'
self._stdin = 1
self._in = sys.stdin
else:
self.infile = self._argv.pop(0)
self._argc -= 1
self._stdin = 0
self._in = open(self.infile, 'r')
return self
def next(self):
"iterator next"
line = self._in.readline()
if line:
self.lineno += 1
self.linesum += 1
return line
self.lineno = -1
self.infile = None
if self._stdin:
raise StopIteration
self._in.close()
if self._argc < 1:
raise StopIteration
self.lineno = 0
self.infile = self._argv.pop(0)
self._argc -= 1
self._in = open(self.infile, 'r')
return self.next()
def warning(self, text):
"print warning message to stderr, possibly with filename and lineno"
if self.lineno > 0:
print >> sys.stderr, '%s:%i: warning: %s' % (self.infile, self.lineno, text)
else:
print >> sys.stderr, '\nWarning %s: %s\n' % (self.progname, text)
def error(self, text):
"print error message to stderr, possibly with filename and lineno, and exit"
if self.lineno > 0:
print >> sys.stderr, '%s:%i: %s' % (self.infile, self.lineno, text)
else:
print >> sys.stderr, '\nError %s: %s\n' % (self.progname, text)
sys.exit(1)
def usage(self):
"print usage message"
print >> sys.stderr, '\n' + self._usage + '\n'
sys.exit(1)
def shift(self):
"pop first of remaining arguments (shift)"
if self._argc < 1:
self.usage()
self._argc -= 1
return self._argv.pop(0)
def pop(self):
"pop last of remaining arguments"
if self._argc < 1:
self.usage()
self._argc -= 1
return self._argv.pop()
def getopt(self, shortopts, longopts=[]):
"get options and merge into dict 'opt'"
options, self._argv = getopt.getopt(self._argv, shortopts, longopts)
self.opt.update(dict(options))
self._argc = len(self._argv)
if __name__ == '__main__':
a = Args('Usage: %(progname)s [-a value] [-b value] [-c] word [file...]')
a.opt['-a'] = 'option a' # set some default option values
a.opt['-b'] = 'option b' #
a.getopt('a:b:c') # get user supplied option values
word = a.shift() # get the first of the remaining arguments
# use a.pop() to get the last instead
for line in a: # iterate over the contents of all remaining arguments (filenames)
if a.lineno == 1:
print 'starting new file:', a.infile
a.warning(line.rstrip())
print 'Options:', a.opt
print 'Word:', word
print 'Total number of lines:', a.linesum
print sys.argv # unchanged
a.warning('warn 1') # print a warning
a.error('error') # print an error message and exit
a.warning('warn 2') # this won't show