Kaydet (Commit) eba20e60 authored tarafından Greg Ward's avatar Greg Ward

Upgrade optparse module and tests to Optik 1.5a1:

  * add expansion of default values in help text: the string
    "%default" in an option's help string is expanded to str() of
    that option's default value, or "none" if no default value.
  * bug #955889: option default values that happen to be strings are
    now processed in the same way as values from the command line; this
    allows generation of nicer help when using custom types.  Can
    be disabled with parser.set_process_default_values(False).
  * bug #960515: don't crash when generating help for callback
    options that specify 'type', but not 'dest' or 'metavar'.
  * feature #815264: change the default help format for short options
    that take an argument from e.g. "-oARG" to "-o ARG"; add
    set_short_opt_delimiter() and set_long_opt_delimiter() methods to
    HelpFormatter to allow (slight) customization of the formatting.
  * patch #736940: internationalize Optik: all built-in user-
    targeted literal strings are passed through gettext.gettext().  (If
    you want translations (.po files), they're not included with Python
    -- you'll find them in the Optik source distribution from
    http://optik.sourceforge.net/ .)
  * bug #878453: respect $COLUMNS environment variable for
    wrapping help output.
  * feature #988122: expand "%prog" in the 'description' passed
    to OptionParser, just like in the 'usage' and 'version' strings.
    (This is *not* done in the 'description' passed to OptionGroup.)
üst 7357222d
......@@ -16,13 +16,11 @@ For support, use the optik-users@lists.sourceforge.net mailing list
# Python developers: please do not make changes to this file, since
# it is automatically generated from the Optik source code.
__version__ = "1.4.1+"
__version__ = "1.5a1"
__all__ = ['Option',
'SUPPRESS_HELP',
'SUPPRESS_USAGE',
'STD_HELP_OPTION',
'STD_VERSION_OPTION',
'Values',
'OptionContainer',
'OptionGroup',
......@@ -37,7 +35,8 @@ __all__ = ['Option',
'BadOptionError']
__copyright__ = """
Copyright (c) 2001-2003 Gregory P. Ward. All rights reserved.
Copyright (c) 2001-2004 Gregory P. Ward. All rights reserved.
Copyright (c) 2002-2004 Python Software Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
......@@ -70,12 +69,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import sys, os
import types
import textwrap
from gettext import gettext as _
def _repr(self):
return "<%s at 0x%x: %s>" % (self.__class__.__name__, id(self), self)
# This file was generated from:
# Id: option_parser.py,v 1.67 2004/07/24 23:21:21 gward Exp
# Id: option.py,v 1.33 2004/07/24 23:21:21 gward Exp
# Id: help.py,v 1.15 2004/07/24 23:21:21 gward Exp
# Id: errors.py,v 1.9 2004/07/24 23:21:21 gward Exp
class OptParseError (Exception):
def __init__ (self, msg):
def __init__(self, msg):
self.msg = msg
def __str__ (self):
def __str__(self):
return self.msg
......@@ -85,11 +95,11 @@ class OptionError (OptParseError):
inconsistent arguments.
"""
def __init__ (self, msg, option):
def __init__(self, msg, option):
self.msg = msg
self.option_id = str(option)
def __str__ (self):
def __str__(self):
if self.option_id:
return "option %s: %s" % (self.option_id, self.msg)
else:
......@@ -120,6 +130,8 @@ class HelpFormatter:
formatting help; by default IndentedHelpFormatter is used.
Instance attributes:
parser : OptionParser
the controlling OptionParser instance
indent_increment : int
the number of columns to indent per nesting level
max_help_position : int
......@@ -128,51 +140,108 @@ class HelpFormatter:
the calculated starting column for option help text;
initially the same as the maximum
width : int
total number of columns for output
total number of columns for output (pass None to constructor for
this value to be taken from the $COLUMNS environment variable)
level : int
current indentation level
current_indent : int
current indentation level (in columns)
help_width : int
number of columns available for option help text (calculated)
default_tag : str
text to replace with each option's default value, "%default"
by default. Set to false value to disable default value expansion.
option_strings : { Option : str }
maps Option instances to the snippet of help text explaining
the syntax of that option, e.g. "-h, --help" or
"-fFILE, --file=FILE"
_short_opt_fmt : str
format string controlling how short options with values are
printed in help text. Must be either "%s%s" ("-fFILE") or
"%s %s" ("-f FILE"), because those are the two syntaxes that
Optik supports.
_long_opt_fmt : str
similar but for long options; must be either "%s %s" ("--file FILE")
or "%s=%s" ("--file=FILE").
"""
def __init__ (self,
indent_increment,
max_help_position,
width,
short_first):
NO_DEFAULT_VALUE = "none"
def __init__(self,
indent_increment,
max_help_position,
width,
short_first):
self.parser = None
self.indent_increment = indent_increment
self.help_position = self.max_help_position = max_help_position
if width is None:
try:
width = int(os.environ['COLUMNS'])
except (KeyError, ValueError):
width = 80
width -= 2
self.width = width
self.current_indent = 0
self.level = 0
self.help_width = width - max_help_position
self.help_width = None # computed later
self.short_first = short_first
self.default_tag = "%default"
self.option_strings = {}
self._short_opt_fmt = "%s %s"
self._long_opt_fmt = "%s=%s"
def set_parser(self, parser):
self.parser = parser
def set_short_opt_delimiter(self, delim):
if delim not in ("", " "):
raise ValueError(
"invalid metavar delimiter for short options: %r" % delim)
self._short_opt_fmt = "%s" + delim + "%s"
def indent (self):
def set_long_opt_delimiter(self, delim):
if delim not in ("=", " "):
raise ValueError(
"invalid metavar delimiter for long options: %r" % delim)
self._long_opt_fmt = "%s" + delim + "%s"
def indent(self):
self.current_indent += self.indent_increment
self.level += 1
def dedent (self):
def dedent(self):
self.current_indent -= self.indent_increment
assert self.current_indent >= 0, "Indent decreased below 0."
self.level -= 1
def format_usage (self, usage):
def format_usage(self, usage):
raise NotImplementedError, "subclasses must implement"
def format_heading (self, heading):
def format_heading(self, heading):
raise NotImplementedError, "subclasses must implement"
def format_description (self, description):
def format_description(self, description):
if not description:
return ""
desc_width = self.width - self.current_indent
indent = " "*self.current_indent
return textwrap.fill(description, desc_width,
return textwrap.fill(description,
desc_width,
initial_indent=indent,
subsequent_indent=indent)
subsequent_indent=indent) + "\n"
def expand_default(self, option):
if self.parser is None or not self.default_tag:
return option.help
default_value = self.parser.defaults.get(option.dest)
if default_value is NO_DEFAULT or default_value is None:
default_value = self.NO_DEFAULT_VALUE
def format_option (self, option):
return option.help.replace(self.default_tag, str(default_value))
def format_option(self, option):
# The help for each option consists of two parts:
# * the opt strings and metavars
# eg. ("-x", or "-fFILENAME, --file=FILENAME")
......@@ -188,7 +257,7 @@ class HelpFormatter:
# -fFILENAME, --file=FILENAME
# read data from FILENAME
result = []
opts = option.option_strings
opts = self.option_strings[option]
opt_width = self.help_position - self.current_indent - 2
if len(opts) > opt_width:
opts = "%*s%s\n" % (self.current_indent, "", opts)
......@@ -198,7 +267,8 @@ class HelpFormatter:
indent_first = 0
result.append(opts)
if option.help:
help_lines = textwrap.wrap(option.help, self.help_width)
help_text = self.expand_default(option)
help_lines = textwrap.wrap(help_text, self.help_width)
result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
result.extend(["%*s%s\n" % (self.help_position, "", line)
for line in help_lines[1:]])
......@@ -206,29 +276,32 @@ class HelpFormatter:
result.append("\n")
return "".join(result)
def store_option_strings (self, parser):
def store_option_strings(self, parser):
self.indent()
max_len = 0
for opt in parser.option_list:
strings = self.format_option_strings(opt)
opt.option_strings = strings
self.option_strings[opt] = strings
max_len = max(max_len, len(strings) + self.current_indent)
self.indent()
for group in parser.option_groups:
for opt in group.option_list:
strings = self.format_option_strings(opt)
opt.option_strings = strings
self.option_strings[opt] = strings
max_len = max(max_len, len(strings) + self.current_indent)
self.dedent()
self.dedent()
self.help_position = min(max_len + 2, self.max_help_position)
self.help_width = self.width - self.help_position
def format_option_strings (self, option):
def format_option_strings(self, option):
"""Return a comma-separated list of option strings & metavariables."""
if option.takes_value():
metavar = option.metavar or option.dest.upper()
short_opts = [sopt + metavar for sopt in option._short_opts]
long_opts = [lopt + "=" + metavar for lopt in option._long_opts]
short_opts = [self._short_opt_fmt % (sopt, metavar)
for sopt in option._short_opts]
long_opts = [self._long_opt_fmt % (lopt, metavar)
for lopt in option._long_opts]
else:
short_opts = option._short_opts
long_opts = option._long_opts
......@@ -244,18 +317,18 @@ class IndentedHelpFormatter (HelpFormatter):
"""Format help with indented section bodies.
"""
def __init__ (self,
indent_increment=2,
max_help_position=24,
width=79,
short_first=1):
def __init__(self,
indent_increment=2,
max_help_position=24,
width=None,
short_first=1):
HelpFormatter.__init__(
self, indent_increment, max_help_position, width, short_first)
def format_usage (self, usage):
return "usage: %s\n" % usage
def format_usage(self, usage):
return _("usage: %s\n") % usage
def format_heading (self, heading):
def format_heading(self, heading):
return "%*s%s:\n" % (self.current_indent, "", heading)
......@@ -263,34 +336,33 @@ class TitledHelpFormatter (HelpFormatter):
"""Format help with underlined section headers.
"""
def __init__ (self,
indent_increment=0,
max_help_position=24,
width=79,
short_first=0):
def __init__(self,
indent_increment=0,
max_help_position=24,
width=None,
short_first=0):
HelpFormatter.__init__ (
self, indent_increment, max_help_position, width, short_first)
def format_usage (self, usage):
return "%s %s\n" % (self.format_heading("Usage"), usage)
def format_usage(self, usage):
return "%s %s\n" % (self.format_heading(_("Usage")), usage)
def format_heading (self, heading):
def format_heading(self, heading):
return "%s\n%s\n" % (heading, "=-"[self.level] * len(heading))
_builtin_cvt = { "int" : (int, "integer"),
"long" : (long, "long integer"),
"float" : (float, "floating-point"),
"complex" : (complex, "complex") }
_builtin_cvt = { "int" : (int, _("integer")),
"long" : (long, _("long integer")),
"float" : (float, _("floating-point")),
"complex" : (complex, _("complex")) }
def check_builtin (option, opt, value):
def check_builtin(option, opt, value):
(cvt, what) = _builtin_cvt[option.type]
try:
return cvt(value)
except ValueError:
raise OptionValueError(
#"%s: invalid %s argument %r" % (opt, what, value))
"option %s: invalid %s value: %r" % (opt, what, value))
_("option %s: invalid %s value: %r") % (opt, what, value))
def check_choice(option, opt, value):
if value in option.choices:
......@@ -298,12 +370,12 @@ def check_choice(option, opt, value):
else:
choices = ", ".join(map(repr, option.choices))
raise OptionValueError(
"option %s: invalid choice: %r (choose from %s)"
_("option %s: invalid choice: %r (choose from %s)")
% (opt, value, choices))
# Not supplying a default is different from a default of None,
# so we need an explicit "not supplied" value.
NO_DEFAULT = "NO"+"DEFAULT"
NO_DEFAULT = ("NO", "DEFAULT")
class Option:
......@@ -392,7 +464,7 @@ class Option:
TYPE_CHECKER = { "int" : check_builtin,
"long" : check_builtin,
"float" : check_builtin,
"complex" : check_builtin,
"complex": check_builtin,
"choice" : check_choice,
}
......@@ -410,7 +482,7 @@ class Option:
# -- Constructor/initialization methods ----------------------------
def __init__ (self, *opts, **attrs):
def __init__(self, *opts, **attrs):
# Set _short_opts, _long_opts attrs from 'opts' tuple.
# Have to be set now, in case no option strings are supplied.
self._short_opts = []
......@@ -429,7 +501,7 @@ class Option:
for checker in self.CHECK_METHODS:
checker(self)
def _check_opt_strings (self, opts):
def _check_opt_strings(self, opts):
# Filter out None because early versions of Optik had exactly
# one short option and one long option, either of which
# could be None.
......@@ -438,7 +510,7 @@ class Option:
raise TypeError("at least one option string must be supplied")
return opts
def _set_opt_strings (self, opts):
def _set_opt_strings(self, opts):
for opt in opts:
if len(opt) < 2:
raise OptionError(
......@@ -459,7 +531,7 @@ class Option:
self)
self._long_opts.append(opt)
def _set_attrs (self, attrs):
def _set_attrs(self, attrs):
for attr in self.ATTRS:
if attrs.has_key(attr):
setattr(self, attr, attrs[attr])
......@@ -477,13 +549,13 @@ class Option:
# -- Constructor validation methods --------------------------------
def _check_action (self):
def _check_action(self):
if self.action is None:
self.action = "store"
elif self.action not in self.ACTIONS:
raise OptionError("invalid action: %r" % self.action, self)
def _check_type (self):
def _check_type(self):
if self.type is None:
# XXX should factor out another class attr here: list of
# actions that *require* a type
......@@ -495,6 +567,12 @@ class Option:
# No type given? "string" is the most sensible default.
self.type = "string"
else:
# Allow type objects as an alternative to their names.
if type(self.type) is type:
self.type = self.type.__name__
if self.type == "str":
self.type = "string"
if self.type not in self.TYPES:
raise OptionError("invalid option type: %r" % self.type, self)
if self.action not in self.TYPED_ACTIONS:
......@@ -514,9 +592,13 @@ class Option:
raise OptionError(
"must not supply choices for type %r" % self.type, self)
def _check_dest (self):
if self.action in self.STORE_ACTIONS and self.dest is None:
# No destination given, and we need one for this action.
def _check_dest(self):
# No destination given, and we need one for this action. The
# self.type check is for callbacks that take a value.
takes_value = (self.action in self.STORE_ACTIONS or
self.type is not None)
if self.dest is None and takes_value:
# Glean a destination from the first long option string,
# or from the first short option string if no long options.
if self._long_opts:
......@@ -525,13 +607,13 @@ class Option:
else:
self.dest = self._short_opts[0][1]
def _check_const (self):
def _check_const(self):
if self.action != "store_const" and self.const is not None:
raise OptionError(
"'const' must not be supplied for action %r" % self.action,
self)
def _check_nargs (self):
def _check_nargs(self):
if self.action in self.TYPED_ACTIONS:
if self.nargs is None:
self.nargs = 1
......@@ -540,7 +622,7 @@ class Option:
"'nargs' must not be supplied for action %r" % self.action,
self)
def _check_callback (self):
def _check_callback(self):
if self.action == "callback":
if not callable(self.callback):
raise OptionError(
......@@ -579,31 +661,42 @@ class Option:
# -- Miscellaneous methods -----------------------------------------
def __str__ (self):
def __str__(self):
return "/".join(self._short_opts + self._long_opts)
def takes_value (self):
__repr__ = _repr
def takes_value(self):
return self.type is not None
def get_opt_string(self):
if self._long_opts:
return self._long_opts[0]
else:
return self._short_opts[0]
# -- Processing methods --------------------------------------------
def check_value (self, opt, value):
def check_value(self, opt, value):
checker = self.TYPE_CHECKER.get(self.type)
if checker is None:
return value
else:
return checker(self, opt, value)
def process (self, opt, value, values, parser):
# First, convert the value(s) to the right type. Howl if any
# value(s) are bogus.
def convert_value(self, opt, value):
if value is not None:
if self.nargs == 1:
value = self.check_value(opt, value)
return self.check_value(opt, value)
else:
value = tuple([self.check_value(opt, v) for v in value])
return tuple([self.check_value(opt, v) for v in value])
def process(self, opt, value, values, parser):
# First, convert the value(s) to the right type. Howl if any
# value(s) are bogus.
value = self.convert_value(opt, value)
# And then take whatever action is expected of us.
# This is a separate method to make life easier for
......@@ -611,7 +704,7 @@ class Option:
return self.take_action(
self.action, self.dest, opt, value, values, parser)
def take_action (self, action, dest, opt, value, values, parser):
def take_action(self, action, dest, opt, value, values, parser):
if action == "store":
setattr(values, dest, value)
elif action == "store_const":
......@@ -642,33 +735,44 @@ class Option:
# 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")
# For compatibility with Python 2.2
try:
True, False
except NameError:
(True, False) = (1, 0)
try:
basestring
except NameError:
basestring = (str, unicode)
class Values:
def __init__ (self, defaults=None):
def __init__(self, defaults=None):
if defaults:
for (attr, val) in defaults.items():
setattr(self, attr, val)
def __repr__ (self):
return ("<%s at 0x%x: %r>"
% (self.__class__.__name__, id(self), self.__dict__))
def __str__(self):
return str(self.__dict__)
__repr__ = _repr
def _update_careful (self, dict):
def __eq__(self, other):
if isinstance(other, Values):
return self.__dict__ == other.__dict__
elif isinstance(other, dict):
return self.__dict__ == other
else:
return false
def __ne__(self, other):
return not (self == other)
def _update_careful(self, dict):
"""
Update the option values from an arbitrary dictionary, but only
use keys from dict that already have a corresponding attribute
......@@ -681,7 +785,7 @@ class Values:
if dval is not None:
setattr(self, attr, dval)
def _update_loose (self, dict):
def _update_loose(self, dict):
"""
Update the option values from an arbitrary dictionary,
using all keys from the dictionary regardless of whether
......@@ -689,7 +793,7 @@ class Values:
"""
self.__dict__.update(dict)
def _update (self, dict, mode):
def _update(self, dict, mode):
if mode == "careful":
self._update_careful(dict)
elif mode == "loose":
......@@ -697,17 +801,17 @@ class Values:
else:
raise ValueError, "invalid update mode: %r" % mode
def read_module (self, modname, mode="careful"):
def read_module(self, modname, mode="careful"):
__import__(modname)
mod = sys.modules[modname]
self._update(vars(mod), mode)
def read_file (self, filename, mode="careful"):
def read_file(self, filename, mode="careful"):
vars = {}
execfile(filename, vars)
self._update(vars, mode)
def ensure_value (self, attr, value):
def ensure_value(self, attr, value):
if not hasattr(self, attr) or getattr(self, attr) is None:
setattr(self, attr, value)
return getattr(self, attr)
......@@ -745,7 +849,7 @@ class OptionContainer:
"""
def __init__ (self, option_class, conflict_handler, description):
def __init__(self, option_class, conflict_handler, description):
# Initialize the option list and related data structures.
# This method must be provided by subclasses, and it must
# initialize at least the following instance attributes:
......@@ -756,7 +860,7 @@ class OptionContainer:
self.set_conflict_handler(conflict_handler)
self.set_description(description)
def _create_option_mappings (self):
def _create_option_mappings(self):
# For use by OptionParser constructor -- create the master
# option mappings used by this OptionParser and all
# OptionGroups that it owns.
......@@ -765,25 +869,28 @@ class OptionContainer:
self.defaults = {} # maps option dest -> default value
def _share_option_mappings (self, parser):
def _share_option_mappings(self, parser):
# For use by OptionGroup constructor -- use shared option
# mappings from the OptionParser that owns this OptionGroup.
self._short_opt = parser._short_opt
self._long_opt = parser._long_opt
self.defaults = parser.defaults
def set_conflict_handler (self, handler):
def set_conflict_handler(self, handler):
if handler not in ("ignore", "error", "resolve"):
raise ValueError, "invalid conflict_resolution value %r" % handler
self.conflict_handler = handler
def set_description (self, description):
def set_description(self, description):
self.description = description
def get_description(self):
return self.description
# -- Option-adding methods -----------------------------------------
def _check_conflict (self, option):
def _check_conflict(self, option):
conflict_opts = []
for opt in option._short_opts:
if self._short_opt.has_key(opt):
......@@ -812,7 +919,7 @@ class OptionContainer:
if not (c_option._short_opts or c_option._long_opts):
c_option.container.option_list.remove(c_option)
def add_option (self, *args, **kwargs):
def add_option(self, *args, **kwargs):
"""add_option(Option)
add_option(opt_str, ..., kwarg=val, ...)
"""
......@@ -842,21 +949,21 @@ class OptionContainer:
return option
def add_options (self, option_list):
def add_options(self, option_list):
for option in option_list:
self.add_option(option)
# -- Option query/removal methods ----------------------------------
def get_option (self, opt_str):
def get_option(self, opt_str):
return (self._short_opt.get(opt_str) or
self._long_opt.get(opt_str))
def has_option (self, opt_str):
def has_option(self, opt_str):
return (self._short_opt.has_key(opt_str) or
self._long_opt.has_key(opt_str))
def remove_option (self, opt_str):
def remove_option(self, opt_str):
option = self._short_opt.get(opt_str)
if option is None:
option = self._long_opt.get(opt_str)
......@@ -872,7 +979,7 @@ class OptionContainer:
# -- Help-formatting methods ---------------------------------------
def format_option_help (self, formatter):
def format_option_help(self, formatter):
if not self.option_list:
return ""
result = []
......@@ -881,38 +988,36 @@ class OptionContainer:
result.append(formatter.format_option(option))
return "".join(result)
def format_description (self, formatter):
if self.description:
return formatter.format_description(self.description)
else:
return ""
def format_description(self, formatter):
return formatter.format_description(self.get_description())
def format_help (self, formatter):
def format_help(self, formatter):
result = []
if self.description:
desc = self.format_description(formatter) + "\n"
else:
desc = ""
return desc + self.format_option_help(formatter)
result.append(self.format_description(formatter))
if self.option_list:
result.append(self.format_option_help(formatter))
return "\n".join(result)
class OptionGroup (OptionContainer):
def __init__ (self, parser, title, description=None):
def __init__(self, parser, title, description=None):
self.parser = parser
OptionContainer.__init__(
self, parser.option_class, parser.conflict_handler, description)
self.title = title
def _create_option_list (self):
def _create_option_list(self):
self.option_list = []
self._share_option_mappings(self.parser)
def set_title (self, title):
def set_title(self, title):
self.title = title
# -- Help-formatting methods ---------------------------------------
def format_help (self, formatter):
def format_help(self, formatter):
result = formatter.format_heading(self.title)
formatter.indent()
result += OptionContainer.format_help(self, formatter)
......@@ -937,7 +1042,12 @@ class OptionParser (OptionContainer):
the name of the current program (to override
os.path.basename(sys.argv[0])).
allow_interspersed_args : boolean = true
option_groups : [OptionGroup]
list of option groups in this parser (option groups are
irrelevant for parsing the command-line, but very useful
for generating help)
allow_interspersed_args : bool = true
if true, positional arguments may be interspersed with options.
Assuming -a and -b each take a single argument, the command-line
-ablah foo bar -bboo baz
......@@ -950,6 +1060,14 @@ class OptionParser (OptionContainer):
Python's getopt module, Perl's Getopt::Std, and other argument-
parsing libraries, but it is generally annoying to users.)
process_default_values : bool = true
if true, option default values are processed similarly to option
values from the command line: that is, they are passed to the
type-checking function for the option's type (as long as the
default value is a string). (This really only matters if you
have defined custom types; see SF bug #955889.) Set it to false
to restore the behaviour of Optik 1.4.1 and earlier.
rargs : [string]
the argument list currently being parsed. Only set when
parse_args() is active, and continually trimmed down as
......@@ -972,30 +1090,32 @@ class OptionParser (OptionContainer):
standard_option_list = []
def __init__ (self,
usage=None,
option_list=None,
option_class=Option,
version=None,
conflict_handler="error",
description=None,
formatter=None,
add_help_option=1,
prog=None):
def __init__(self,
usage=None,
option_list=None,
option_class=Option,
version=None,
conflict_handler="error",
description=None,
formatter=None,
add_help_option=True,
prog=None):
OptionContainer.__init__(
self, option_class, conflict_handler, description)
self.set_usage(usage)
self.prog = prog
self.version = version
self.allow_interspersed_args = 1
self.allow_interspersed_args = True
self.process_default_values = True
if formatter is None:
formatter = IndentedHelpFormatter()
self.formatter = formatter
self.formatter.set_parser(self)
# 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.
# argument, and (if applicable) the _add_version_option() and
# _add_help_option() methods.
self._populate_option_list(option_list,
add_help=add_help_option)
......@@ -1004,65 +1124,90 @@ class OptionParser (OptionContainer):
# -- Private methods -----------------------------------------------
# (used by our or OptionContainer's constructor)
def _create_option_list (self):
def _create_option_list(self):
self.option_list = []
self.option_groups = []
self._create_option_mappings()
def _populate_option_list (self, option_list, add_help=1):
def _add_help_option(self):
self.add_option("-h", "--help",
action="help",
help=_("show this help message and exit"))
def _add_version_option(self):
self.add_option("--version",
action="version",
help=_("show program's version number and exit"))
def _populate_option_list(self, option_list, add_help=True):
if self.standard_option_list:
self.add_options(self.standard_option_list)
if option_list:
self.add_options(option_list)
if self.version:
self.add_option(STD_VERSION_OPTION)
self._add_version_option()
if add_help:
self.add_option(STD_HELP_OPTION)
self._add_help_option()
def _init_parsing_state (self):
def _init_parsing_state(self):
# These are set in parse_args() for the convenience of callbacks.
self.rargs = None
self.largs = None
self.values = None
def _get_prog_name(self):
if self.prog:
return self.prog
else:
return get_prog_name()
# -- Simple modifier methods ---------------------------------------
def set_usage (self, usage):
def set_usage(self, usage):
if usage is None:
self.usage = "%prog [options]"
self.usage = _("%prog [options]")
elif usage is SUPPRESS_USAGE:
self.usage = None
elif usage.lower().startswith("usage: "):
# for backwards compatibility with Optik 1.3 and earlier
# For backwards compatibility with Optik 1.3 and earlier.
elif usage.startswith("usage:" + " "):
self.usage = usage[7:]
else:
self.usage = usage
def enable_interspersed_args (self):
self.allow_interspersed_args = 1
def enable_interspersed_args(self):
self.allow_interspersed_args = True
def disable_interspersed_args(self):
self.allow_interspersed_args = False
def disable_interspersed_args (self):
self.allow_interspersed_args = 0
def set_process_default_values(self, process):
self.process_default_values = process
def set_default (self, dest, value):
def set_default(self, dest, value):
self.defaults[dest] = value
def set_defaults (self, **kwargs):
def set_defaults(self, **kwargs):
self.defaults.update(kwargs)
def get_default_values (self):
return Values(self.defaults)
def _get_all_options(self):
options = self.option_list[:]
for group in self.option_groups:
options.extend(group.option_list)
return options
def get_default_values(self):
if not self.process_default_values:
# Old, pre-Optik 1.5 behaviour.
return Values(self.defaults)
defaults = self.defaults.copy()
for option in self._get_all_options():
default = defaults.get(option.dest)
if isinstance(default, basestring):
opt_str = option.get_opt_string()
defaults[option.dest] = option.check_value(opt_str, default)
return Values(defaults)
# -- OptionGroup methods -------------------------------------------
def add_option_group (self, *args, **kwargs):
def add_option_group(self, *args, **kwargs):
# XXX lots of overlap with OptionContainer.add_option()
if type(args[0]) is types.StringType:
group = OptionGroup(self, *args, **kwargs)
......@@ -1078,7 +1223,7 @@ class OptionParser (OptionContainer):
self.option_groups.append(group)
return group
def get_option_group (self, opt_str):
def get_option_group(self, opt_str):
option = (self._short_opt.get(opt_str) or
self._long_opt.get(opt_str))
if option and option.container is not self:
......@@ -1088,13 +1233,13 @@ class OptionParser (OptionContainer):
# -- Option-parsing methods ----------------------------------------
def _get_args (self, args):
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):
def parse_args(self, args=None, values=None):
"""
parse_args(args : [string] = sys.argv[1:],
values : Values = None)
......@@ -1133,7 +1278,7 @@ class OptionParser (OptionContainer):
args = largs + rargs
return self.check_values(values, args)
def check_values (self, values, args):
def check_values(self, values, args):
"""
check_values(values : Values, args : [string])
-> (values : Values, args : [string])
......@@ -1146,7 +1291,7 @@ class OptionParser (OptionContainer):
"""
return (values, args)
def _process_args (self, largs, rargs, values):
def _process_args(self, largs, rargs, values):
"""_process_args(largs : [string],
rargs : [string],
values : Values)
......@@ -1197,7 +1342,7 @@ class OptionParser (OptionContainer):
# *empty* -- still a subset of [arg0, ..., arg(i-1)], but
# not a very interesting subset!
def _match_long_opt (self, opt):
def _match_long_opt(self, opt):
"""_match_long_opt(opt : string) -> string
Determine which long option string 'opt' matches, ie. which one
......@@ -1206,7 +1351,7 @@ class OptionParser (OptionContainer):
"""
return _match_abbrev(opt, self._long_opt)
def _process_long_opt (self, rargs, values):
def _process_long_opt(self, rargs, values):
arg = rargs.pop(0)
# Value explicitly attached to arg? Pretend it's the next
......@@ -1214,10 +1359,10 @@ class OptionParser (OptionContainer):
if "=" in arg:
(opt, next_arg) = arg.split("=", 1)
rargs.insert(0, next_arg)
had_explicit_value = 1
had_explicit_value = True
else:
opt = arg
had_explicit_value = 0
had_explicit_value = False
opt = self._match_long_opt(opt)
option = self._long_opt[opt]
......@@ -1225,9 +1370,9 @@ class OptionParser (OptionContainer):
nargs = option.nargs
if len(rargs) < nargs:
if nargs == 1:
self.error("%s option requires a value" % opt)
self.error(_("%s option requires an argument") % opt)
else:
self.error("%s option requires %d values"
self.error(_("%s option requires %d arguments")
% (opt, nargs))
elif nargs == 1:
value = rargs.pop(0)
......@@ -1236,16 +1381,16 @@ class OptionParser (OptionContainer):
del rargs[0:nargs]
elif had_explicit_value:
self.error("%s option does not take a value" % opt)
self.error(_("%s option does not take a value") % opt)
else:
value = None
option.process(opt, value, values, self)
def _process_short_opts (self, rargs, values):
def _process_short_opts(self, rargs, values):
arg = rargs.pop(0)
stop = 0
stop = False
i = 1
for ch in arg[1:]:
opt = "-" + ch
......@@ -1253,20 +1398,20 @@ class OptionParser (OptionContainer):
i += 1 # we have consumed a character
if not option:
self.error("no such option: %s" % opt)
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.
if i < len(arg):
rargs.insert(0, arg[i:])
stop = 1
stop = True
nargs = option.nargs
if len(rargs) < nargs:
if nargs == 1:
self.error("%s option requires a value" % opt)
self.error(_("%s option requires an argument") % opt)
else:
self.error("%s option requires %s values"
self.error(_("%s option requires %d arguments")
% (opt, nargs))
elif nargs == 1:
value = rargs.pop(0)
......@@ -1285,7 +1430,19 @@ class OptionParser (OptionContainer):
# -- Feedback methods ----------------------------------------------
def error (self, msg):
def get_prog_name(self):
if self.prog is None:
return os.path.basename(sys.argv[0])
else:
return self.prog
def expand_prog_name(self, s):
return s.replace("%prog", self.get_prog_name())
def get_description(self):
return self.expand_prog_name(self.description)
def error(self, msg):
"""error(msg : string)
Print a usage message incorporating 'msg' to stderr and exit.
......@@ -1293,16 +1450,17 @@ class OptionParser (OptionContainer):
should either exit or raise an exception.
"""
self.print_usage(sys.stderr)
sys.exit("%s: error: %s" % (self._get_prog_name(), msg))
sys.stderr.write("%s: error: %s\n" % (self.get_prog_name(), msg))
sys.exit(2) # command-line usage error
def get_usage (self):
def get_usage(self):
if self.usage:
return self.formatter.format_usage(
self.usage.replace("%prog", self._get_prog_name()))
self.expand_prog_name(self.usage))
else:
return ""
def print_usage (self, file=None):
def print_usage(self, file=None):
"""print_usage(file : file = stdout)
Print the usage message for the current program (self.usage) to
......@@ -1314,13 +1472,13 @@ class OptionParser (OptionContainer):
if self.usage:
print >>file, self.get_usage()
def get_version (self):
def get_version(self):
if self.version:
return self.version.replace("%prog", self._get_prog_name())
return self.expand_prog_name(self.version)
else:
return ""
def print_version (self, file=None):
def print_version(self, file=None):
"""print_version(file : file = stdout)
Print the version message for this program (self.version) to
......@@ -1331,12 +1489,12 @@ class OptionParser (OptionContainer):
if self.version:
print >>file, self.get_version()
def format_option_help (self, formatter=None):
def format_option_help(self, formatter=None):
if formatter is None:
formatter = self.formatter
formatter.store_option_strings(self)
result = []
result.append(formatter.format_heading("options"))
result.append(formatter.format_heading(_("options")))
formatter.indent()
if self.option_list:
result.append(OptionContainer.format_option_help(self, formatter))
......@@ -1348,7 +1506,7 @@ class OptionParser (OptionContainer):
# Drop the last "\n", or the header if no options or option groups:
return "".join(result[:-1])
def format_help (self, formatter=None):
def format_help(self, formatter=None):
if formatter is None:
formatter = self.formatter
result = []
......@@ -1359,7 +1517,7 @@ class OptionParser (OptionContainer):
result.append(self.format_option_help(formatter))
return "".join(result)
def print_help (self, file=None):
def print_help(self, file=None):
"""print_help(file : file = stdout)
Print an extended help message, listing all options and any
......@@ -1372,7 +1530,7 @@ class OptionParser (OptionContainer):
# class OptionParser
def _match_abbrev (s, wordmap):
def _match_abbrev(s, wordmap):
"""_match_abbrev(s : string, wordmap : {string : Option}) -> string
Return the string key in 'wordmap' for which 's' is an unambiguous
......@@ -1390,10 +1548,10 @@ def _match_abbrev (s, wordmap):
if len(possibilities) == 1:
return possibilities[0]
elif not possibilities:
raise BadOptionError("no such option: %s" % s)
raise BadOptionError(_("no such option: %s") % s)
else:
# More than one possible completion: ambiguous prefix.
raise BadOptionError("ambiguous option: %s (%s?)"
raise BadOptionError(_("ambiguous option: %s (%s?)")
% (s, ", ".join(possibilities)))
......
......@@ -20,14 +20,7 @@ from test import test_support
from optparse import make_option, Option, IndentedHelpFormatter, \
TitledHelpFormatter, OptionParser, OptionContainer, OptionGroup, \
SUPPRESS_HELP, SUPPRESS_USAGE, OptionError, OptionConflictError, \
BadOptionError, OptionValueError
from optparse import _match_abbrev
# Do the right thing with boolean values for all known Python versions.
try:
True, False
except NameError:
(True, False) = (1, 0)
BadOptionError, OptionValueError, _match_abbrev
class BaseTest(unittest.TestCase):
def assertParseOK(self, args, expected_opts, expected_positional_args):
......@@ -60,50 +53,62 @@ Args were %(args)s.""" % locals ())
return (options, positional_args)
def assertRaises(self, func, expected_exception, expected_output,
get_output=None,
funcargs=[], funckwargs={}):
def assertRaises(self,
func,
args,
kwargs,
expected_exception,
expected_output,
get_output=None,
exact_match=False):
"""Assert the expected exception is raised when calling a function.
Also check whether the right error message is given for a given error.
Keyword arguments:
func -- The function to be called.
expected_exception -- The exception that should be raised.
expected_output -- The output we expect to see.
get_output -- The function to call to get the output.
funcargs -- The arguments `func` should be called with.
funckwargs -- The keyword arguments `func` should be called with.
Arguments:
func -- the function to call
args -- positional arguments to `func`
kwargs -- keyword arguments to `func`
expected_exception -- exception that should be raised
expected_output -- output we expect to see
get_output -- function to call to get the output
exact_match -- whether output must exactly match expected output,
or merely contain it
Returns the exception raised for further testing.
"""
if args is None:
args = ()
if kwargs is None:
kwargs = {}
if get_output is None:
get_output = self.exception
try:
out = func(*funcargs, **funckwargs)
out = func(*args, **kwargs)
except expected_exception, err:
output = get_output(err)
self.failUnless(output.find(expected_output) != -1,
"""
Message was:
%(output)s
Should contain:
%(expected_output)s
Function called:
%(func)s
With args/kwargs:
%(funcargs)s/%(funckwargs)s""" % locals())
actual_output = get_output(err)
if exact_match:
match = actual_output == expected_exception
else:
match = actual_output.find(expected_output) != -1
self.assert_(match,
"""mismatched output
expected output:
'''%(expected_output)s'''
actual output:
'''%(actual_output)s'''
""" % locals())
return err
else:
self.fail("""
No %(expected_exception)s raised.
Function called:
%(func)s
With args/kwargs:
%(funcargs)s/%(funckwargs)s""" % locals ())
self.fail("""expected exception %(expected_exception)s not raised
called %(func)r
with args %(args)r
and kwargs %(kwargs)r
""" % locals ())
# -- Functions to be used as the get_output argument to assertRaises ------
......@@ -113,23 +118,38 @@ With args/kwargs:
def redirected_stdout(self, err):
return sys.stdout.getvalue()
def redirected_stderr(self, err):
return sys.stderr.getvalue()
# -- Assertions used in more than one class --------------------
def assertParseFail(self, cmdline_args, expected_output):
"""Assert the parser fails with the expected message."""
self.assertRaises(self.parser.parse_args, SystemExit, expected_output,
funcargs=[cmdline_args])
sys.stderr = StringIO()
self.assertRaises(self.parser.parse_args, (cmdline_args,), None,
SystemExit, expected_output,
self.redirected_stderr)
sys.stderr = sys.__stderr__
def assertStdoutEquals(self, cmdline_args, expected_output):
"""Assert the parser prints the expected output on stdout."""
sys.stdout = StringIO()
self.assertRaises(self.parser.parse_args, SystemExit, expected_output,
self.redirected_stdout, [cmdline_args])
self.assertRaises(self.parser.parse_args, (cmdline_args,), None,
SystemExit, expected_output,
self.redirected_stdout)
sys.stdout = sys.__stdout__
def assertTypeError(self, func, expected_output, *args):
"""Assert a TypeError is raised when executing func."""
self.assertRaises(func, TypeError, expected_output, funcargs=args)
self.assertRaises(func, args, None, TypeError, expected_output)
def assertHelp(self, parser, expected_help):
actual_help = parser.format_help()
if actual_help != expected_help:
raise self.failureException(
'help text failure; expected:\n"' +
expected_help + '"; got:\n"' +
actual_help + '"\n')
# -- Test make_option() aka Option -------------------------------------
......@@ -142,8 +162,8 @@ class TestOptionChecks(BaseTest):
self.parser = OptionParser(usage=SUPPRESS_USAGE)
def assertOptionError(self, expected_output, args=[], kwargs={}):
self.assertRaises(make_option, OptionError, expected_output,
funcargs=args, funckwargs=kwargs)
self.assertRaises(make_option, args, kwargs,
OptionError, expected_output)
def test_opt_string_empty(self):
self.assertTypeError(make_option,
......@@ -175,6 +195,8 @@ class TestOptionChecks(BaseTest):
def test_type_invalid(self):
self.assertOptionError("invalid option type: 'foo'",
["-b"], {'type': 'foo'})
self.assertOptionError("invalid option type: 'tuple'",
["-b"], {'type': tuple})
def test_no_type_for_action(self):
self.assertOptionError("must not supply a type for action 'count'",
......@@ -304,8 +326,204 @@ class TestOptionParser(BaseTest):
self.assert_removed()
def test_remove_nonexistent(self):
self.assertRaises(self.parser.remove_option, ValueError,
"no such option 'foo'", funcargs=['foo'])
self.assertRaises(self.parser.remove_option, ('foo',), None,
ValueError, "no such option 'foo'")
class TestTypeAliases(BaseTest):
def setUp(self):
self.parser = OptionParser()
def test_type_aliases(self):
self.parser.add_option("-x", type=int)
self.parser.add_option("-s", type=str)
self.parser.add_option("-t", type="str")
self.assertEquals(self.parser.get_option("-x").type, "int")
self.assertEquals(self.parser.get_option("-s").type, "string")
self.assertEquals(self.parser.get_option("-t").type, "string")
# Custom type for testing processing of default values.
_time_units = { 's' : 1, 'm' : 60, 'h' : 60*60, 'd' : 60*60*24 }
def _check_duration(option, opt, value):
try:
if value[-1].isdigit():
return int(value)
else:
return int(value[:-1]) * _time_units[value[-1]]
except ValueError, IndexError:
raise OptionValueError(
'option %s: invalid duration: %r' % (opt, value))
class DurationOption(Option):
TYPES = Option.TYPES + ('duration',)
TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
TYPE_CHECKER['duration'] = _check_duration
class TestDefaultValues(BaseTest):
def setUp(self):
self.parser = OptionParser()
self.parser.add_option("-v", "--verbose", default=True)
self.parser.add_option("-q", "--quiet", dest='verbose')
self.parser.add_option("-n", type="int", default=37)
self.parser.add_option("-m", type="int")
self.parser.add_option("-s", default="foo")
self.parser.add_option("-t")
self.parser.add_option("-u", default=None)
self.expected = { 'verbose': True,
'n': 37,
'm': None,
's': "foo",
't': None,
'u': None }
def test_basic_defaults(self):
self.assertEqual(self.parser.get_default_values(), self.expected)
def test_mixed_defaults_post(self):
self.parser.set_defaults(n=42, m=-100)
self.expected.update({'n': 42, 'm': -100})
self.assertEqual(self.parser.get_default_values(), self.expected)
def test_mixed_defaults_pre(self):
self.parser.set_defaults(x="barf", y="blah")
self.parser.add_option("-x", default="frob")
self.parser.add_option("-y")
self.expected.update({'x': "frob", 'y': "blah"})
self.assertEqual(self.parser.get_default_values(), self.expected)
self.parser.remove_option("-y")
self.parser.add_option("-y", default=None)
self.expected.update({'y': None})
self.assertEqual(self.parser.get_default_values(), self.expected)
def test_process_default(self):
self.parser.option_class = DurationOption
self.parser.add_option("-d", type="duration", default=300)
self.parser.add_option("-e", type="duration", default="6m")
self.parser.set_defaults(n="42")
self.expected.update({'d': 300, 'e': 360, 'n': 42})
self.assertEqual(self.parser.get_default_values(), self.expected)
self.parser.set_process_default_values(False)
self.expected.update({'d': 300, 'e': "6m", 'n': "42"})
self.assertEqual(self.parser.get_default_values(), self.expected)
class TestProgName(BaseTest):
"""
Test that %prog expands to the right thing in usage, version,
and help strings.
"""
def assertUsage(self, parser, expected_usage):
self.assertEqual(parser.get_usage(), expected_usage)
def assertVersion(self, parser, expected_version):
self.assertEqual(parser.get_version(), expected_version)
def test_default_progname(self):
# Make sure that program name taken from sys.argv[0] by default.
sys.argv[0] = "/foo/bar/baz.py"
parser = OptionParser("usage: %prog ...", version="%prog 1.2")
expected_usage = "usage: baz.py ...\n"
self.assertUsage(parser, expected_usage)
self.assertVersion(parser, "baz.py 1.2")
self.assertHelp(parser,
expected_usage + "\n" +
"options:\n"
" --version show program's version number and exit\n"
" -h, --help show this help message and exit\n")
def test_custom_progname(self):
parser = OptionParser(prog="thingy",
version="%prog 0.1",
usage="%prog arg arg")
parser.remove_option("-h")
parser.remove_option("--version")
expected_usage = "usage: thingy arg arg\n"
self.assertUsage(parser, expected_usage)
self.assertVersion(parser, "thingy 0.1")
self.assertHelp(parser, expected_usage + "\n")
class TestExpandDefaults(BaseTest):
def setUp(self):
self.parser = OptionParser(prog="test")
self.help_prefix = """\
usage: test [options]
options:
-h, --help show this help message and exit
"""
self.file_help = "read from FILE [default: %default]"
self.expected_help_file = self.help_prefix + \
" -f FILE, --file=FILE read from FILE [default: foo.txt]\n"
self.expected_help_none = self.help_prefix + \
" -f FILE, --file=FILE read from FILE [default: none]\n"
def test_option_default(self):
self.parser.add_option("-f", "--file",
default="foo.txt",
help=self.file_help)
self.assertHelp(self.parser, self.expected_help_file)
def test_parser_default_1(self):
self.parser.add_option("-f", "--file",
help=self.file_help)
self.parser.set_default('file', "foo.txt")
self.assertHelp(self.parser, self.expected_help_file)
def test_parser_default_2(self):
self.parser.add_option("-f", "--file",
help=self.file_help)
self.parser.set_defaults(file="foo.txt")
self.assertHelp(self.parser, self.expected_help_file)
def test_no_default(self):
self.parser.add_option("-f", "--file",
help=self.file_help)
self.assertHelp(self.parser, self.expected_help_none)
def test_default_none_1(self):
self.parser.add_option("-f", "--file",
default=None,
help=self.file_help)
self.assertHelp(self.parser, self.expected_help_none)
def test_default_none_2(self):
self.parser.add_option("-f", "--file",
help=self.file_help)
self.parser.set_defaults(file=None)
self.assertHelp(self.parser, self.expected_help_none)
def test_float_default(self):
self.parser.add_option(
"-p", "--prob",
help="blow up with probability PROB [default: %default]")
self.parser.set_defaults(prob=0.43)
expected_help = self.help_prefix + \
" -p PROB, --prob=PROB blow up with probability PROB [default: 0.43]\n"
self.assertHelp(self.parser, expected_help)
def test_alt_expand(self):
self.parser.add_option("-f", "--file",
default="foo.txt",
help="read from FILE [default: *DEFAULT*]")
self.parser.formatter.default_tag = "*DEFAULT*"
self.assertHelp(self.parser, self.expected_help_file)
def test_no_expand(self):
self.parser.add_option("-f", "--file",
default="foo.txt",
help="read from %default file")
self.parser.formatter.default_tag = None
expected_help = self.help_prefix + \
" -f FILE, --file=FILE read from %default file\n"
self.assertHelp(self.parser, expected_help)
# -- Test parser.parse_args() ------------------------------------------
......@@ -318,7 +536,7 @@ class TestStandard(BaseTest):
self.parser = OptionParser(usage=SUPPRESS_USAGE, option_list=options)
def test_required_value(self):
self.assertParseFail(["-a"], "-a option requires a value")
self.assertParseFail(["-a"], "-a option requires an argument")
def test_invalid_integer(self):
self.assertParseFail(["-b", "5x"],
......@@ -580,7 +798,7 @@ class TestNArgs(BaseTest):
def test_nargs_required_values(self):
self.assertParseFail(["--point", "1.0", "3.5"],
"--point option requires 3 values")
"--point option requires 3 arguments")
class TestNArgsAppend(BaseTest):
def setUp(self):
......@@ -597,7 +815,7 @@ class TestNArgsAppend(BaseTest):
def test_nargs_append_required_values(self):
self.assertParseFail(["-f4,3"],
"-f option requires 2 values")
"-f option requires 2 arguments")
def test_nargs_append_simple(self):
self.assertParseOK(["--foo=3", "4"],
......@@ -612,22 +830,6 @@ class TestVersion(BaseTest):
self.assertStdoutEquals(["--version"], "bar 0.1\n")
sys.argv[0] = oldargv
def test_version_with_prog_keyword(self):
oldargv = sys.argv[0]
sys.argv[0] = "./foo/bar"
self.parser = OptionParser(usage=SUPPRESS_USAGE, version="%prog 0.1",
prog="splat")
self.assertStdoutEquals(["--version"], "splat 0.1\n")
sys.argv[0] = oldargv
def test_version_with_prog_attribute(self):
oldargv = sys.argv[0]
sys.argv[0] = "./foo/bar"
self.parser = OptionParser(usage=SUPPRESS_USAGE, version="%prog 0.1")
self.parser.prog = "splat"
self.assertStdoutEquals(["--version"], "splat 0.1\n")
sys.argv[0] = oldargv
def test_no_version(self):
self.parser = OptionParser(usage=SUPPRESS_USAGE)
self.assertParseFail(["--version"],
......@@ -673,8 +875,8 @@ class TestOptionGroup(BaseTest):
def test_add_group_wrong_parser(self):
group = OptionGroup(self.parser, "Spam")
group.parser = OptionParser()
self.assertRaises(self.parser.add_option_group, ValueError,
"invalid OptionGroup (wrong parser)", funcargs=[group])
self.assertRaises(self.parser.add_option_group, (group,), None,
ValueError, "invalid OptionGroup (wrong parser)")
def test_group_manipulate(self):
group = self.parser.add_option_group("Group 2",
......@@ -794,7 +996,22 @@ class TestCallback(BaseTest):
{'filename': "foo", 'x': 42},
[])
class TestCallBackExtraArgs(BaseTest):
def test_callback_help(self):
# This test was prompted by SF bug #960515 -- the point is
# not to inspect the help text, just to make sure that
# format_help() doesn't crash.
parser = OptionParser(usage=SUPPRESS_USAGE)
parser.remove_option("-h")
parser.add_option("-t", "--test", action="callback",
callback=lambda: None, type="string",
help="foo")
expected_help = ("options:\n"
" -t TEST, --test=TEST foo\n")
self.assertHelp(parser, expected_help)
class TestCallbackExtraArgs(BaseTest):
def setUp(self):
options = [make_option("-p", "--point", action="callback",
callback=self.process_tuple,
......@@ -819,7 +1036,7 @@ class TestCallBackExtraArgs(BaseTest):
{'points': [(1,2,3), (4,5,6)]},
[])
class TestCallBackMeddleArgs(BaseTest):
class TestCallbackMeddleArgs(BaseTest):
def setUp(self):
options = [make_option(str(x), action="callback",
callback=self.process_n, dest='things')
......@@ -848,7 +1065,7 @@ class TestCallBackMeddleArgs(BaseTest):
{'things': [('foo', '--')]},
[2])
class TestCallBackManyArgs(BaseTest):
class TestCallbackManyArgs(BaseTest):
def setUp(self):
options = [make_option("-a", "--apple", action="callback", nargs=2,
callback=self.process_many, type="string"),
......@@ -870,10 +1087,10 @@ class TestCallBackManyArgs(BaseTest):
self.assertParseOK(["-a", "foo", "bar", "--apple", "ding", "dong",
"-b", "1", "2", "3", "--bob", "-666", "42",
"0"],
{},
{"apple": None, "bob": None},
[])
class TestCallBackCheckAbbrev(BaseTest):
class TestCallbackCheckAbbrev(BaseTest):
def setUp(self):
self.parser = OptionParser()
self.parser.add_option("--foo-bar", action="callback",
......@@ -885,7 +1102,7 @@ class TestCallBackCheckAbbrev(BaseTest):
def test_abbrev_callback_expansion(self):
self.assertParseOK(["--foo"], {}, [])
class TestCallBackVarArgs(BaseTest):
class TestCallbackVarArgs(BaseTest):
def setUp(self):
options = [make_option("-a", type="int", nargs=2, dest="a"),
make_option("-b", action="store_true", dest="b"),
......@@ -950,13 +1167,12 @@ class ConflictBase(BaseTest):
class TestConflict(ConflictBase):
"""Use the default conflict resolution for Optik 1.2: error."""
def assert_conflict_error(self, func):
err = self.assertRaises(func, OptionConflictError,
"option -v/--version: conflicting option "
"string(s): -v",
funcargs=["-v", "--version"],
funckwargs={'action':"callback",
'callback':self.show_version,
'help':"show version"})
err = self.assertRaises(
func, ("-v", "--version"), {'action' : "callback",
'callback' : self.show_version,
'help' : "show version"},
OptionConflictError,
"option -v/--version: conflicting option string(s): -v")
self.assertEqual(err.msg, "conflicting option string(s): -v")
self.assertEqual(err.option_id, "-v/--version")
......@@ -969,9 +1185,9 @@ class TestConflict(ConflictBase):
self.assert_conflict_error(group.add_option)
def test_no_such_conflict_handler(self):
self.assertRaises(self.parser.set_conflict_handler, ValueError,
"invalid conflict_resolution value 'foo'",
funcargs=['foo'])
self.assertRaises(
self.parser.set_conflict_handler, ('foo',), None,
ValueError, "invalid conflict_resolution value 'foo'")
class TestConflictIgnore(ConflictBase):
......@@ -1082,8 +1298,60 @@ options:
# -- Other testing. ----------------------------------------------------
_expected_help_basic = """\
usage: bar.py [options]
options:
-a APPLE throw APPLEs at basket
-b NUM, --boo=NUM shout "boo!" NUM times (in order to frighten away all the
evil spirits that cause trouble and mayhem)
--foo=FOO store FOO in the foo list for later fooing
-h, --help show this help message and exit
"""
_expected_help_long_opts_first = """\
usage: bar.py [options]
options:
-a APPLE throw APPLEs at basket
--boo=NUM, -b NUM shout "boo!" NUM times (in order to frighten away all the
evil spirits that cause trouble and mayhem)
--foo=FOO store FOO in the foo list for later fooing
--help, -h show this help message and exit
"""
_expected_help_title_formatter = """\
Usage
=====
bar.py [options]
options
=======
-a APPLE throw APPLEs at basket
--boo=NUM, -b NUM shout "boo!" NUM times (in order to frighten away all the
evil spirits that cause trouble and mayhem)
--foo=FOO store FOO in the foo list for later fooing
--help, -h show this help message and exit
"""
_expected_help_short_lines = """\
usage: bar.py [options]
options:
-a APPLE throw APPLEs at basket
-b NUM, --boo=NUM shout "boo!" NUM times (in order to
frighten away all the evil spirits
that cause trouble and mayhem)
--foo=FOO store FOO in the foo list for later
fooing
-h, --help show this help message and exit
"""
class TestHelp(BaseTest):
def setUp(self):
self.parser = self.make_parser(80)
def make_parser(self, columns):
options = [
make_option("-a", type="string", dest='a',
metavar="APPLE", help="throw APPLEs at basket"),
......@@ -1095,9 +1363,8 @@ class TestHelp(BaseTest):
make_option("--foo", action="append", type="string", dest='foo',
help="store FOO in the foo list for later fooing"),
]
usage = "%prog [options]"
self.parser = OptionParser(usage=usage, option_list=options)
os.environ['COLUMNS'] = str(columns)
return OptionParser(option_list=options)
def assertHelpEquals(self, expected_output):
# This trick is used to make optparse believe bar.py is being executed.
......@@ -1109,62 +1376,30 @@ class TestHelp(BaseTest):
sys.argv[0] = oldargv
def test_help(self):
self.assertHelpEquals("""\
usage: bar.py [options]
options:
-aAPPLE throw APPLEs at basket
-bNUM, --boo=NUM shout "boo!" NUM times (in order to frighten away all
the evil spirits that cause trouble and mayhem)
--foo=FOO store FOO in the foo list for later fooing
-h, --help show this help message and exit
""")
self.assertHelpEquals(_expected_help_basic)
def test_help_old_usage(self):
self.parser.set_usage("usage: %prog [options]")
self.assertHelpEquals("""\
usage: bar.py [options]
options:
-aAPPLE throw APPLEs at basket
-bNUM, --boo=NUM shout "boo!" NUM times (in order to frighten away all
the evil spirits that cause trouble and mayhem)
--foo=FOO store FOO in the foo list for later fooing
-h, --help show this help message and exit
""")
self.assertHelpEquals(_expected_help_basic)
def test_help_long_opts_first(self):
self.parser.formatter.short_first = 0
self.assertHelpEquals("""\
usage: bar.py [options]
options:
-aAPPLE throw APPLEs at basket
--boo=NUM, -bNUM shout "boo!" NUM times (in order to frighten away all
the evil spirits that cause trouble and mayhem)
--foo=FOO store FOO in the foo list for later fooing
--help, -h show this help message and exit
""")
self.assertHelpEquals(_expected_help_long_opts_first)
def test_help_title_formatter(self):
self.parser.formatter = TitledHelpFormatter()
self.assertHelpEquals("""\
Usage
=====
bar.py [options]
self.assertHelpEquals(_expected_help_title_formatter)
options
=======
-aAPPLE throw APPLEs at basket
--boo=NUM, -bNUM shout "boo!" NUM times (in order to frighten away all
the evil spirits that cause trouble and mayhem)
--foo=FOO store FOO in the foo list for later fooing
--help, -h show this help message and exit
""")
def test_wrap_columns(self):
# Ensure that wrapping respects $COLUMNS environment variable.
# Need to reconstruct the parser, since that's the only time
# we look at $COLUMNS.
self.parser = self.make_parser(60)
self.assertHelpEquals(_expected_help_short_lines)
def test_help_description_groups(self):
self.parser.set_description(
"This is the program description. This program has "
"This is the program description for %prog. %prog has "
"an option group as well as single options.")
group = OptionGroup(
......@@ -1177,21 +1412,26 @@ options
self.assertHelpEquals("""\
usage: bar.py [options]
This is the program description. This program has an option group as well as
single options.
This is the program description for bar.py. bar.py has an option group as
well as single options.
options:
-aAPPLE throw APPLEs at basket
-bNUM, --boo=NUM shout "boo!" NUM times (in order to frighten away all
the evil spirits that cause trouble and mayhem)
--foo=FOO store FOO in the foo list for later fooing
-h, --help show this help message and exit
-a APPLE throw APPLEs at basket
-b NUM, --boo=NUM shout "boo!" NUM times (in order to frighten away all the
evil spirits that cause trouble and mayhem)
--foo=FOO store FOO in the foo list for later fooing
-h, --help show this help message and exit
Dangerous Options:
Caution: use of these options is at your own risk. It is believed that
some of them bite.
-g Group option.
Caution: use of these options is at your own risk. It is believed
that some of them bite.
-g Group option.
""")
class TestMatchAbbrev(BaseTest):
def test_match_abbrev(self):
self.assertEqual(_match_abbrev("--f",
......@@ -1205,15 +1445,23 @@ class TestMatchAbbrev(BaseTest):
s = "--f"
wordmap = {"--foz": None, "--foo": None, "--fie": None}
possibilities = ", ".join(wordmap.keys())
self.assertRaises(_match_abbrev, BadOptionError,
"ambiguous option: --f (%s?)" % possibilities,
funcargs=[s, wordmap])
self.assertRaises(
_match_abbrev, (s, wordmap), None,
BadOptionError, "ambiguous option: --f (%s?)" % possibilities)
def test_main():
def _testclasses():
mod = sys.modules[__name__]
test_support.run_unittest(
*[getattr(mod, name) for name in dir(mod) if name.startswith('Test')]
)
return [getattr(mod, name) for name in dir(mod) if name.startswith('Test')]
def suite():
suite = unittest.TestSuite()
for testclass in _testclasses():
suite.addTest(unittest.makeSuite(testclass))
return suite
def test_main():
test_support.run_suite(suite())
if __name__ == '__main__':
unittest.main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment