a better getopt than getopt.py? [long]
Roberto@lupex.net
Roberto
Thu Feb 22 03:55:35 EST 2001
Sean 'Shaleh' Perry <shaleh at valinux.com> wrote:
>this means I have my options defined twice and then once again in the USAGE
>output. Is there not a better way to handle arguments?
I have written the attached module as this issue bothers me too.
The code is not well tested (its very recent).
Here's a commented example from a program that uses it:
# -------------------- Assign default for command line args
# you can put them in any python dictionary
# ( globals() is the dictionary in this case )
low_uids_level = 0
root_check = 1
dup_UIDs = 1
dup_names = 1
verbose = 0
ignore_list = []
passwd_file = '/etc/passwd'
# -------------------- Create a command line parser
cmd = CmdLine2.CmdLine('Passwd file checks', # program title
usage_msg='[options]', # usage message
# (default: "[options] args")
context = globals() # where to store option values
# should be a dict-like object
# (default: an empty dict)
# accessible thru cmd.context
)
cmd.add_help_option() # add a -h --help option
# to print the list of available options
cmd.add_set_option( # an option to set
('low_uids_level',string.atoi), # context['low_uids_level']
['l:','low-uid='], # to the integer value
"Check for UIDs < [argument]") # of its argument
# if the first argument of cmd_add_XXX_option() is a tuple,
# the first element is the variable name/item key,
# the second elemnt is a conversion function that is applied to the arg
cmd.add_toggle_option( # a boolean flag option, as in:
'root_check', # root_check = not root_check
['r','root'],
"Enable/disable report of superusers")
cmd.add_check_option( # a boolean set option:
'verbose', # verbose = 1
['v','verbose'],
"Print configution before processing")
cmd.add_append_option( # append arguments to ignore_list
'ignore_list',
['i:','ignore='],
"Add the specified user to the ignore list")
def option_handler(opt, arg):
# do something sensible with opt and arg
pass
cmd.add_action_option( # custom option that
option_handler, # execute option_handler(opt,arg)
['bingo'],
"Prints lucky numbers for bingo")
cmd.getopt() # parse the command line
# the rest of the program
if verbose:
print "I'll check", passwd_file,":"
if low_uids_level > 0:
print " - for UIDs <", low_uid_level
if root_check:
print " - for root users"
if dup_UIDs:
print " - for duplicated UIDs"
if dup_names:
print " - for duplicated usernames"
print "Ignore list:", list2str(ignore_list)
print
...
Here's the help message of the program (generated by add_help_option):
Passwd file checks
chk-passwd [options]
-h --help Print this help message
-l: --low-uid= Check for UIDs < [argument]
-r --root Enable/disable report of superusers
-u --uids En/dis report of duplicated UIDs
-n --names En/dis report of duplicated usernames
-v --verbose Print configution before processing
-i: --ignore= Add the specified user to the ignore list
-p: --passwd= Process the specified file (default: /etc/passwd)
(the complete script has a few more options than the example code above)
And here's the CmdLine2.py module:
# CmdLine2 - Command Line Processing v2.0
#
#
# 22-feb-2001 - Released as public domain code
# by Roberto Lupi <Roberto at lupi.ws>
import sys
import getopt
import re
import os
debug = 0
short_option_re = re.compile('.:?$')
def RemoveLast(st, postfix):
if st[-len(postfix):] == postfix:
return st[:-len(postfix)]
else:
return st
def ListToStr(lst, sep=' '):
return reduce( lambda a,b,sep=sep: str(a)+sep+str(b), lst)
######################################################################
##
## Class Option
##
######################################################################
class Option:
def __init__(self, opts, help_msg):
short_op = []
long_op = []
for opt in opts:
if short_option_re.match(opt):
short_op.append(opt)
else:
long_op.append(opt)
self.short_op = short_op
self.long_op = long_op
self.help_msg = help_msg
def fire(self, opt, arg):
pass
def short_options(self, arg_spec=None,op_prefix='-'):
if self.short_op:
if arg_spec:
return map( lambda x,op_prefix=op_prefix: op_prefix + \
x, self.short_op )
else:
return map( lambda x,op_prefix=op_prefix: op_prefix + \
RemoveLast(x,':'), self.short_op )
else:
return []
def long_options(self, arg_spec=None,op_prefix='--'):
if self.long_op:
if arg_spec:
return map( lambda x,op_prefix=op_prefix: op_prefix + \
x, self.long_op )
else:
return map( lambda x,op_prefix=op_prefix: op_prefix + \
RemoveLast(x,'='), self.long_op )
else:
return []
def all_options(self, arg_spec=None):
return self.short_options(arg_spec) + self.long_options(arg_spec)
def print_help(self):
if self.help_msg:
print " %-20s %s" % (ListToStr(self.all_options(1)), \
self.help_msg)
######################################################################
##
## Class OptionAction
##
######################################################################
class OptionAction(Option):
def __init__(self, action, ops, help_msg):
Option.__init__(self,ops,help_msg)
self.action = action
def fire(self, opt, arg):
apply(self.action, (opt, arg))
######################################################################
##
## Class OptionContextBased
##
######################################################################
class OptionContextBased(Option):
def __init__(self, action_spec, context, ops, help_msg):
Option.__init__(self,ops,help_msg)
self.action_spec = action_spec
self.context = context
######################################################################
##
## Class OptionSet
##
######################################################################
class OptionSet(OptionContextBased):
def fire(self, opt, arg):
if type(self.action_spec) == type(""):
vname = self.action_spec
cvt = lambda x: x
else:
(vname, cvt) = self.action_spec
self.context[vname] = cvt(arg)
######################################################################
##
## Class OptionToggle
##
######################################################################
class OptionToggle(OptionContextBased):
def fire(self, opt, arg):
vname = self.action_spec
if self.context.has_key(vname):
old_value = self.context[vname]
else:
old_value = 0
self.context[vname] = not old_value
######################################################################
##
## Class OptionCheck
##
######################################################################
class OptionCheck(OptionContextBased):
def fire(self, opt, arg):
vname = self.action_spec
self.context[vname] = 1
######################################################################
##
## Class OptionAppend
##
######################################################################
class OptionAppend(OptionContextBased):
def fire(self, opt, arg):
if type(self.action_spec) == type(""):
vname = self.action_spec
cvt = lambda x: x
else:
(vname, cvt) = self.action_spec
if self.context.has_key(vname) and self.context[vname]:
self.context[vname].append(cvt(arg))
else:
self.context[vname] = [ cvt(arg) ]
######################################################################
##
## Class CmdLine
##
######################################################################
class CmdLine:
def __init__(self, program_title=None, usage_msg="[options] files", \
context={}):
self.program_title = program_title
self.usage_msg = usage_msg
self.options = []
self.context = context
def getopt(self, args=None):
if self.program_title:
print self.program_title
print
if args is None:
args = sys.argv[1:]
# build option map
option_map = {}
short_ops = ""
long_ops = []
for opt in self.options:
if opt.short_op:
short_ops = short_ops + \
ListToStr(opt.short_options(arg_spec=1,op_prefix=''),'')
if opt.long_op:
long_ops.extend(opt.long_options(arg_spec=1,op_prefix=''))
for op in opt.all_options():
option_map[op] = opt
if debug:
print "OPTION MAP:"
for op_spec, Opt in option_map.items():
print " %10s -> %s" % (op_spec, str(Opt))
# process cmd line
try:
opts, other_args = getopt.getopt(args, short_ops, long_ops)
for opt, arg in opts:
Op = option_map[opt]
if debug:
print "DEBUG: Firing %s (opt=%s, arg=%s)" % \
(str(Op), opt, arg)
Op.fire(opt, arg)
return other_args
except getopt.error, e:
print str(e)
sys.exit(1)
def print_help(self, opt=None, arg=None):
print "%s %s" % (os.path.basename(sys.argv[0]), self.usage_msg)
print
for opt in self.options:
opt.print_help()
print
if opt: # invoked thru -h
sys.exit(1)
def add_option(self, Opt):
self.options.append(Opt)
def add_help_option(self):
self.add_option(OptionAction(self.print_help,['h','help'], \
"Print this help message"))
def add_action_option(self, action, ops, help_msg=None):
self.add_option(OptionAction(action,ops,help_msg))
def add_set_option(self, vname, ops, help_msg=None):
self.add_option(OptionSet(vname,self.context,ops,help_msg))
def add_toggle_option(self, vname, ops, help_msg=None):
self.add_option(OptionToggle(vname,self.context,ops,help_msg))
def add_check_option(self, vname, ops, help_msg=None):
self.add_option(OptionCheck(vname,self.context,ops,help_msg))
def add_append_option(self, vname, ops, help_msg=None):
self.add_option(OptionAppend(vname,self.context,ops,help_msg))
--
Roberto Lupi
More information about the Python-list
mailing list