Re: [Python-Dev] anyone tried to use optparse with something else than sys.argv?
Erik Heneryd <[email protected]> Tue, 10 Dec 2002 21:52:36 +0100 (CET)
| Newsgroups | gmane.comp.python.optik.user |
|---|---|
| Message-ID | <Pine.LNX.4.44.0212102026300.4771-200000@brain.secret.pythonware.com> |
On Tue, 10 Dec 2002, Greg Ward wrote: > [cc'ing [email protected], since this concerns > Optik's reincarnation as optparse in the Python standard library] > > > well, to me, it's pretty obvious that the design effort on optparse has > > pretty much been focused strictly on initial sys.argv parsing and while > > it's extendable, other use cases (embedded consoles etc) seem to have been > > given little or no real thought. > > Guilty as charged. My intention was always to support that kind of > stuff, but I've never had occasion to test it myself. Sounds like I > carefully hid lots of little bugs in there -- congrats for finding them. > ;-) :) well, i guess it's not so much about bugs, as it's about keeping it simple. optparse seems quite complex in it's design and i don't want to subclass, override etc if i could have done the same thing just by catching an exception. i don't know, maybe i'm just stuck thinking in getopt ways... :) well, i've tried to apply all those things in a patch (attached): can't say i fully understand optparse, it's quite complex, so i thought i'd post it here for comments before anything else... the main thing i've done is that i've centralized the error handling. if something goes wrong, error() is not called, but an exception raised. all exceptions are then caught in parse_args(). there one can decide what to do with them - call error() or really throw an exception? the help and version actions no longer call methods, they too raise exceptions, to be caught in parse_args(). the standard help and version Options are created on the fly and therefore of the right option_class type. concrete api change: * opobj.parse_args(list, values, throw=0) - list is supposed to be the whole sys.argv (ie including the command). - opobj.prog = list[0]. - if throw is true, BadOptionError and OptionValueError is raised instead of calling error(). - if throw is true, OptionMessage is raised with the message as contents, instead of printing and exiting. i really don't know about all this, but is seemed like the right thing to do. i think those, small, simple customizations will be much easier, while still able to do that heavy duty class-stuff... what do you think? erik
optparse.diff
(text/plain, 11.9 KB)
Index: optparse.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/optparse.py,v
retrieving revision 1.1
diff -c -r1.1 optparse.py
*** optparse.py 14 Nov 2002 22:00:19 -0000 1.1
--- optparse.py 10 Dec 2002 19:25:20 -0000
***************
*** 82,87 ****
--- 82,93 ----
"""
Raised if an invalid or ambiguous option is seen on the command-line.
"""
+
+ class OptionMessage (Exception):
+ """
+ Used when a valid, message triggering, option is seen on the command
+ line. For example, show help or version."""
+
class HelpFormatter:
"""
***************
*** 621,650 ****
kwargs = self.callback_kwargs or {}
self.callback(self, opt, value, parser, *args, **kwargs)
elif action == "help":
! parser.print_help()
! sys.exit(0)
elif action == "version":
! parser.print_version()
! sys.exit(0)
else:
raise RuntimeError, "unknown action %r" % self.action
return 1
# class Option
- def get_prog_name ():
- return os.path.basename(sys.argv[0])
SUPPRESS_HELP = "SUPPRESS"+"HELP"
SUPPRESS_USAGE = "SUPPRESS"+"USAGE"
! STD_HELP_OPTION = Option("-h", "--help",
! action="help",
! help="show this help message and exit")
! STD_VERSION_OPTION = Option("--version",
! action="version",
! help="show program's version number and exit")
class Values:
--- 627,653 ----
kwargs = self.callback_kwargs or {}
self.callback(self, opt, value, parser, *args, **kwargs)
elif action == "help":
! raise OptionMessage, parser.format_help()
elif action == "version":
! raise OptionMessage, parser.get_version()
else:
raise RuntimeError, "unknown action %r" % self.action
return 1
# class Option
SUPPRESS_HELP = "SUPPRESS"+"HELP"
SUPPRESS_USAGE = "SUPPRESS"+"USAGE"
! STD_HELP_OPTS = ("-h", "--help")
! STD_HELP_ATTRS = {"action": "help",
! "help": "show this help message and exit"}
!
! STD_VERSION_OPTS = ("--version",)
! STD_VERSION_ATTRS = {"action": "version",
! "help": "show program's version number and exit"}
class Values:
***************
*** 979,986 ****
# Populate the option list; initial sources are the
# standard_option_list class attribute, the 'option_list'
! # argument, and the STD_VERSION_OPTION (if 'version' supplied)
! # and STD_HELP_OPTION globals.
self._populate_option_list(option_list,
add_help=add_help_option)
--- 982,990 ----
# Populate the option list; initial sources are the
# standard_option_list class attribute, the 'option_list'
! # argument.
! # A standard help option (and if 'version' supplied, a
! # standard version option) will also be created and added.
self._populate_option_list(option_list,
add_help=add_help_option)
***************
*** 1000,1008 ****
if option_list:
self.add_options(option_list)
if self.version:
! self.add_option(STD_VERSION_OPTION)
if add_help:
! self.add_option(STD_HELP_OPTION)
def _init_parsing_state (self):
# These are set in parse_args() for the convenience of callbacks.
--- 1004,1012 ----
if option_list:
self.add_options(option_list)
if self.version:
! self.add_option(*STD_VERSION_OPTS, **STD_VERSION_ATTRS)
if add_help:
! self.add_option(*STD_HELP_OPTS, **STD_HELP_ATTRS)
def _init_parsing_state (self):
# These are set in parse_args() for the convenience of callbacks.
***************
*** 1068,1082 ****
# -- Option-parsing methods ----------------------------------------
! def _get_args (self, args):
! if args is None:
! return sys.argv[1:]
else:
! return args[:] # don't modify caller's list
! def parse_args (self, args=None, values=None):
"""
! parse_args(args : [string] = sys.argv[1:],
values : Values = None)
-> (values : Values, args : [string])
--- 1072,1087 ----
# -- Option-parsing methods ----------------------------------------
! def _get_prog_name (self, args):
! if args:
! return os.path.basename(args[0])
else:
! # no args, fall back on the empty string
! return ""
! def parse_args (self, args=sys.argv, values=None, throw=0):
"""
! parse_args(args : [string] = sys.argv,
values : Values = None)
-> (values : Values, args : [string])
***************
*** 1088,1094 ****
your option values) and 'args' is the list of arguments left
over after parsing options.
"""
- rargs = self._get_args(args)
if values is None:
values = self.get_default_values()
--- 1093,1098 ----
***************
*** 1101,1114 ****
# the leftover arguments -- ie. what's left after removing
# options and their arguments (the "l" stands for "leftover"
# or "left-hand")
! self.rargs = rargs
self.largs = largs = []
self.values = values
try:
stop = self._process_args(largs, rargs, values)
except (BadOptionError, OptionValueError), err:
! self.error(err.msg)
args = largs + rargs
return self.check_values(values, args)
--- 1105,1128 ----
# the leftover arguments -- ie. what's left after removing
# options and their arguments (the "l" stands for "leftover"
# or "left-hand")
! self.rargs = rargs = args[1:]
self.largs = largs = []
self.values = values
+ self.prog = self._get_prog_name(args)
try:
stop = self._process_args(largs, rargs, values)
except (BadOptionError, OptionValueError), err:
! if throw:
! raise
! else:
! self.error(err.msg)
! except OptionMessage, err:
! if throw:
! raise
! else:
! print >> sys.stdout, err
! sys.exit(0)
args = largs + rargs
return self.check_values(values, args)
***************
*** 1205,1214 ****
nargs = option.nargs
if len(rargs) < nargs:
if nargs == 1:
! self.error("%s option requires a value" % opt)
else:
! self.error("%s option requires %d values"
! % (opt, nargs))
elif nargs == 1:
value = rargs.pop(0)
else:
--- 1219,1227 ----
nargs = option.nargs
if len(rargs) < nargs:
if nargs == 1:
! raise OptionValueError, "%s option requires a value" % opt
else:
! raise OptionValueError, "%s option requires %d values" % (opt, nargs)
elif nargs == 1:
value = rargs.pop(0)
else:
***************
*** 1216,1222 ****
del rargs[0:nargs]
elif had_explicit_value:
! self.error("%s option does not take a value" % opt)
else:
value = None
--- 1229,1235 ----
del rargs[0:nargs]
elif had_explicit_value:
! raise OptionValueError, "%s option does not take a value" % opt
else:
value = None
***************
*** 1233,1239 ****
i += 1 # we have consumed a character
if not option:
! self.error("no such option: %s" % opt)
if option.takes_value():
# Any characters left in arg? Pretend they're the
# next arg, and stop consuming characters of arg.
--- 1246,1252 ----
i += 1 # we have consumed a character
if not option:
! raise BadOptionError, "no such option: %s" % opt
if option.takes_value():
# Any characters left in arg? Pretend they're the
# next arg, and stop consuming characters of arg.
***************
*** 1244,1253 ****
nargs = option.nargs
if len(rargs) < nargs:
if nargs == 1:
! self.error("%s option requires a value" % opt)
else:
! self.error("%s option requires %s values"
! % (opt, nargs))
elif nargs == 1:
value = rargs.pop(0)
else:
--- 1257,1265 ----
nargs = option.nargs
if len(rargs) < nargs:
if nargs == 1:
! raise OptionValueError, "%s option requires a value" % opt
else:
! raise OptionValueError, "%s option requires %s values" % (opt, nargs)
elif nargs == 1:
value = rargs.pop(0)
else:
***************
*** 1273,1284 ****
should either exit or raise an exception.
"""
self.print_usage(sys.stderr)
! sys.exit("%s: error: %s" % (get_prog_name(), msg))
def get_usage (self):
if self.usage:
return self.formatter.format_usage(
! self.usage.replace("%prog", get_prog_name()))
else:
return ""
--- 1285,1296 ----
should either exit or raise an exception.
"""
self.print_usage(sys.stderr)
! sys.exit("%s: error: %s" % (self.prog, msg))
def get_usage (self):
if self.usage:
return self.formatter.format_usage(
! self.usage.replace("%prog", self.prog))
else:
return ""
***************
*** 1296,1302 ****
def get_version (self):
if self.version:
! return self.version.replace("%prog", get_prog_name())
else:
return ""
--- 1308,1314 ----
def get_version (self):
if self.version:
! return self.version.replace("%prog", self.prog)
else:
return ""
***************
*** 1339,1352 ****
result.append(self.format_option_help(formatter))
return "".join(result)
! def print_help (self, file=None):
"""print_help(file : file = stdout)
Print an extended help message, listing all options and any
help text provided with them, to 'file' (default stdout).
"""
- if file is None:
- file = sys.stdout
file.write(self.format_help())
# class OptionParser
--- 1351,1362 ----
result.append(self.format_option_help(formatter))
return "".join(result)
! def print_help (self, file=sys.stdout):
"""print_help(file : file = stdout)
Print an extended help message, listing all options and any
help text provided with them, to 'file' (default stdout).
"""
file.write(self.format_help())
# class OptionParser