From ab05edc0d1fd6369cb6aa6175af68e0c0c00c60a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 23 Apr 2006 03:47:58 +0000 Subject: [PATCH] Update optparse to Optik 1.5.1. --- Doc/lib/liboptparse.tex | 403 +++++++++++++++++++++++++++++++------- Lib/optparse.py | 204 ++++++++++++++----- Lib/test/test_optparse.py | 200 +++++++++++++------ Misc/NEWS | 5 + 4 files changed, 633 insertions(+), 179 deletions(-) diff --git a/Doc/lib/liboptparse.tex b/Doc/lib/liboptparse.tex index 8aca501f75..ec43e3df90 100644 --- a/Doc/lib/liboptparse.tex +++ b/Doc/lib/liboptparse.tex @@ -35,9 +35,9 @@ With these few lines of code, users of your script can now do the \end{verbatim} As it parses the command line, \code{optparse} sets attributes of the -\var{options} object returned by \method{parse{\_}args()} based on user-supplied +\code{options} object returned by \method{parse{\_}args()} based on user-supplied command-line values. When \method{parse{\_}args()} returns from parsing this -command line, \var{options.filename} will be \code{"outfile"} and +command line, \code{options.filename} will be \code{"outfile"} and \code{options.verbose} will be \code{False}. \code{optparse} supports both long and short options, allows short options to be merged together, and allows options to be associated with their arguments in a variety of @@ -100,8 +100,8 @@ options; the traditional \UNIX{} syntax is a hyphen (``-'') followed by a single letter, e.g. \code{"-x"} or \code{"-F"}. Also, traditional \UNIX{} syntax allows multiple options to be merged into a single argument, e.g. \code{"-x -F"} is equivalent to \code{"-xF"}. The GNU project -introduced \code{"{--}"} followed by a series of hyphen-separated words, -e.g. \code{"{--}file"} or \code{"{--}dry-run"}. These are the only two option +introduced \code{"-{}-"} followed by a series of hyphen-separated words, +e.g. \code{"-{}-file"} or \code{"-{}-dry-run"}. These are the only two option syntaxes provided by \module{optparse}. Some other option syntaxes that the world has seen include: @@ -170,7 +170,7 @@ For example, consider this hypothetical command-line: prog -v --report /tmp/report.txt foo bar \end{verbatim} -\code{"-v"} and \code{"{--}report"} are both options. Assuming that +\code{"-v"} and \code{"-{}-report"} are both options. Assuming that \longprogramopt{report} takes one argument, \code{"/tmp/report.txt"} is an option argument. \code{"foo"} and \code{"bar"} are positional arguments. @@ -287,12 +287,12 @@ but that's rarely necessary: by default it uses \code{sys.argv{[}1:]}.) \method{parse{\_}args()} returns two values: \begin{itemize} \item {} -\var{options}, an object containing values for all of your options{---}e.g. if \code{"-{}-file"} takes a single string argument, then -\var{options.file} will be the filename supplied by the user, or +\code{options}, an object containing values for all of your options{---}e.g. if \code{"-{}-file"} takes a single string argument, then +\code{options.file} will be the filename supplied by the user, or \code{None} if the user did not supply that option \item {} -\var{args}, the list of positional arguments leftover after parsing +\code{args}, the list of positional arguments leftover after parsing options \end{itemize} @@ -309,7 +309,7 @@ command line. There is a fixed set of actions hard-coded into \module{optparse} adding new actions is an advanced topic covered in section~\ref{optparse-extending}, Extending \module{optparse}. Most actions tell \module{optparse} to store a value in some variable{---}for example, take a string from the command line and store it in an -attribute of \var{options}. +attribute of \code{options}. If you don't specify an option action, \module{optparse} defaults to \code{store}. @@ -333,8 +333,8 @@ args = ["-f", "foo.txt"] \end{verbatim} When \module{optparse} sees the option string \code{"-f"}, it consumes the next -argument, \code{"foo.txt"}, and stores it in \var{options.filename}. So, -after this call to \method{parse{\_}args()}, \var{options.filename} is +argument, \code{"foo.txt"}, and stores it in \code{options.filename}. So, +after this call to \method{parse{\_}args()}, \code{options.filename} is \code{"foo.txt"}. Some other option types supported by \module{optparse} are \code{int} and \code{float}. @@ -379,7 +379,7 @@ types is covered in section~\ref{optparse-extending}, Extending \module{optparse Flag options{---}set a variable to true or false when a particular option is seen{---}are quite common. \module{optparse} supports them with two separate actions, \code{store{\_}true} and \code{store{\_}false}. For example, you might have a -\var{verbose} flag that is turned on with \code{"-v"} and off with \code{"-q"}: +\code{verbose} flag that is turned on with \code{"-v"} and off with \code{"-q"}: \begin{verbatim} parser.add_option("-v", action="store_true", dest="verbose") parser.add_option("-q", action="store_false", dest="verbose") @@ -421,7 +421,7 @@ want more control. \module{optparse} lets you supply a default value for each destination, which is assigned before the command line is parsed. First, consider the verbose/quiet example. If we want \module{optparse} to set -\var{verbose} to \code{True} unless \code{"-q"} is seen, then we can do this: +\code{verbose} to \code{True} unless \code{"-q"} is seen, then we can do this: \begin{verbatim} parser.add_option("-v", action="store_true", dest="verbose", default=True) parser.add_option("-q", action="store_false", dest="verbose") @@ -441,7 +441,7 @@ parser.add_option("-v", action="store_true", dest="verbose", default=False) parser.add_option("-q", action="store_false", dest="verbose", default=True) \end{verbatim} -Again, the default value for \var{verbose} will be \code{True}: the last +Again, the default value for \code{verbose} will be \code{True}: the last default value supplied for any particular destination is the one that counts. @@ -566,7 +566,7 @@ argument to OptionParser: parser = OptionParser(usage="%prog [-f] [-q]", version="%prog 1.0") \end{verbatim} -Note that \code{"{\%}prog"} is expanded just like it is in \var{usage}. Apart +Note that \code{"{\%}prog"} is expanded just like it is in \code{usage}. Apart from that, \code{version} can contain anything you like. When you supply it, \module{optparse} automatically adds a \code{"-{}-version"} option to your parser. If it encounters this option on the command line, it expands your @@ -580,14 +580,14 @@ foo 1.0 \end{verbatim} -\subsubsection{How \module{optparse} handles errors\label{optparse-how-optik-handles-errors}} +\subsubsection{How \module{optparse} handles errors\label{optparse-how-optparse-handles-errors}} There are two broad classes of errors that \module{optparse} has to worry about: programmer errors and user errors. Programmer errors are usually -erroneous calls to \code{parse.add{\_}option()}, e.g. invalid option strings, +erroneous calls to \code{parser.add{\_}option()}, e.g. invalid option strings, unknown option attributes, missing option attributes, etc. These are dealt with in the usual way: raise an exception (either -\exception{optparse.OptionError} or \exception{TypeError}) and let the program crash. +\code{optparse.OptionError} or \code{TypeError}) and let the program crash. Handling user errors is much more important, since they are guaranteed to happen no matter how stable your code is. \module{optparse} can automatically @@ -659,12 +659,66 @@ def main(): if __name__ == "__main__": main() \end{verbatim} -% $Id: tutorial.txt 415 2004-09-30 02:26:17Z greg $ +% $Id: tutorial.txt 505 2005-07-22 01:52:40Z gward $ \subsection{Reference Guide\label{optparse-reference-guide}} +\subsubsection{Creating the parser\label{optparse-creating-parser}} + +The first step in using \module{optparse} is to create an OptionParser instance: +\begin{verbatim} +parser = OptionParser(...) +\end{verbatim} + +The OptionParser constructor has no required arguments, but a number of +optional keyword arguments. You should always pass them as keyword +arguments, i.e. do not rely on the order in which the arguments are +declared. +\begin{quote} +\begin{description} +\item[\code{usage} (default: \code{"{\%}prog {[}options]"})] +The usage summary to print when your program is run incorrectly or +with a help option. When \module{optparse} prints the usage string, it expands +\code{{\%}prog} to \code{os.path.basename(sys.argv{[}0])} (or to \code{prog} if +you passed that keyword argument). To suppress a usage message, +pass the special value \code{optparse.SUPPRESS{\_}USAGE}. +\item[\code{option{\_}list} (default: \code{{[}]})] +A list of Option objects to populate the parser with. The options +in \code{option{\_}list} are added after any options in +\code{standard{\_}option{\_}list} (a class attribute that may be set by +OptionParser subclasses), but before any version or help options. +Deprecated; use \method{add{\_}option()} after creating the parser instead. +\item[\code{option{\_}class} (default: optparse.Option)] +Class to use when adding options to the parser in \method{add{\_}option()}. +\item[\code{version} (default: \code{None})] +A version string to print when the user supplies a version option. +If you supply a true value for \code{version}, \module{optparse} automatically adds +a version option with the single option string \code{"-{}-version"}. The +substring \code{"{\%}prog"} is expanded the same as for \code{usage}. +\item[\code{conflict{\_}handler} (default: \code{"error"})] +Specifies what to do when options with conflicting option strings +are added to the parser; see section~\ref{optparse-conflicts-between-options}, Conflicts between options. +\item[\code{description} (default: \code{None})] +A paragraph of text giving a brief overview of your program. \module{optparse} +reformats this paragraph to fit the current terminal width and +prints it when the user requests help (after \code{usage}, but before +the list of options). +\item[\code{formatter} (default: a new IndentedHelpFormatter)] +An instance of optparse.HelpFormatter that will be used for +printing help text. \module{optparse} provides two concrete classes for this +purpose: IndentedHelpFormatter and TitledHelpFormatter. +\item[\code{add{\_}help{\_}option} (default: \code{True})] +If true, \module{optparse} will add a help option (with option strings \code{"-h"} +and \code{"-{}-help"}) to the parser. +\item[\code{prog}] +The string to use when expanding \code{"{\%}prog"} in \code{usage} and +\code{version} instead of \code{os.path.basename(sys.argv{[}0])}. +\end{description} +\end{quote} + + \subsubsection{Populating the parser\label{optparse-populating-parser}} There are several ways to populate the parser with options. The @@ -708,38 +762,34 @@ strings, e.g. \programopt{-f} and \longprogramopt{file}. You can specify any number of short or long option strings, but you must specify at least one overall option string. -The canonical way to create an Option instance is by calling -\function{make{\_}option()}, so that is what will be shown here. However, the -most common and convenient way is to use \code{parser.add{\_}option()}. Note -that \function{make{\_}option()} and \code{parser.add{\_}option()} have identical call -signatures: +The canonical way to create an Option instance is with the +\method{add{\_}option()} method of \class{OptionParser}: \begin{verbatim} -make_option(opt_str, ..., attr=value, ...) -parser.add_option(opt_str, ..., attr=value, ...) +parser.add_option(opt_str[, ...], attr=value, ...) \end{verbatim} To define an option with only a short option string: \begin{verbatim} -make_option("-f", attr=value, ...) +parser.add_option("-f", attr=value, ...) \end{verbatim} And to define an option with only a long option string: \begin{verbatim} -make_option("--foo", attr=value, ...) +parser.add_option("--foo", attr=value, ...) \end{verbatim} -The \code{attr=value} keyword arguments define option attributes, -i.e. attributes of the Option object. The most important option -attribute is \member{action}, and it largely determines what other attributes -are relevant or required. If you pass irrelevant option attributes, or -fail to pass required ones, \module{optparse} raises an OptionError exception -explaining your mistake. +The keyword arguments define attributes of the new Option object. The +most important option attribute is \member{action}, and it largely determines +which other attributes are relevant or required. If you pass irrelevant +option attributes, or fail to pass required ones, \module{optparse} raises an +OptionError exception explaining your mistake. -An options's \emph{action} determines what \module{optparse} does when it encounters -this option on the command-line. The actions hard-coded into \module{optparse} are: +An options's \emph{action} determines what \module{optparse} does when it encounters this +option on the command-line. The standard option actions hard-coded into +\module{optparse} are: \begin{description} \item[\code{store}] -store this option's argument {[}default] +store this option's argument (default) \item[\code{store{\_}const}] store a constant value \item[\code{store{\_}true}] @@ -748,6 +798,8 @@ store a true value store a false value \item[\code{append}] append this option's argument to a list +\item[\code{append{\_}const}] +append a constant value to a list \item[\code{count}] increment a counter by one \item[\code{callback}] @@ -762,24 +814,25 @@ action, you may also supply \member{type} and \member{dest} option attributes; s below.) As you can see, most actions involve storing or updating a value -somewhere. \module{optparse} always creates an instance of \code{optparse.Values} -specifically for this purpose; we refer to this instance as \var{options}. -Option arguments (and various other values) are stored as attributes of -this object, according to the \member{dest} (destination) option attribute. +somewhere. \module{optparse} always creates a special object for this, +conventionally called \code{options} (it happens to be an instance of +\code{optparse.Values}). Option arguments (and various other values) are +stored as attributes of this object, according to the \member{dest} +(destination) option attribute. For example, when you call \begin{verbatim} parser.parse_args() \end{verbatim} -one of the first things \module{optparse} does is create the \var{options} object: +one of the first things \module{optparse} does is create the \code{options} object: \begin{verbatim} options = Values() \end{verbatim} If one of the options in this parser is defined with \begin{verbatim} -make_option("-f", "--file", action="store", type="string", dest="filename") +parser.add_option("-f", "--file", action="store", type="string", dest="filename") \end{verbatim} and the command-line being parsed includes any of the following: @@ -790,8 +843,7 @@ and the command-line being parsed includes any of the following: --file foo \end{verbatim} -then \module{optparse}, on seeing the \programopt{-f} or \longprogramopt{file} option, will do the -equivalent of +then \module{optparse}, on seeing this option, will do the equivalent of \begin{verbatim} options.filename = "foo" \end{verbatim} @@ -911,6 +963,13 @@ If, a little later on, \code{"-{}-tracks=4"} is seen, it does: options.tracks.append(int("4")) \end{verbatim} +\item {} +\code{append{\_}const} {[}required: \code{const}; relevant: \member{dest}] + +Like \code{store{\_}const}, but the value \code{const} is appended to \member{dest}; +as with \code{append}, \member{dest} defaults to \code{None}, and an an empty list is +automatically created the first time the option is encountered. + \item {} \code{count} {[}relevant: \member{dest}] @@ -939,14 +998,9 @@ options.verbosity += 1 \code{callback} {[}required: \code{callback}; relevant: \member{type}, \code{nargs}, \code{callback{\_}args}, \code{callback{\_}kwargs}] -Call the function specified by \code{callback}. The signature of -this function should be +Call the function specified by \code{callback}, which is called as \begin{verbatim} -func(option : Option, - opt : string, - value : any, - parser : OptionParser, - *args, **kwargs) +func(option, opt_str, value, parser, *args, **kwargs) \end{verbatim} See section~\ref{optparse-option-callbacks}, Option Callbacks for more detail. @@ -956,7 +1010,7 @@ See section~\ref{optparse-option-callbacks}, Option Callbacks for more detail. Prints a complete help message for all the options in the current option parser. The help message is constructed from -the \var{usage} string passed to OptionParser's constructor and +the \code{usage} string passed to OptionParser's constructor and the \member{help} string passed to every option. If no \member{help} string is supplied for an option, it will still be @@ -1007,6 +1061,87 @@ constructor. As with \member{help} options, you will rarely create \end{itemize} +\subsubsection{Option attributes\label{optparse-option-attributes}} + +The following option attributes may be passed as keyword arguments +to \code{parser.add{\_}option()}. If you pass an option attribute +that is not relevant to a particular option, or fail to pass a required +option attribute, \module{optparse} raises OptionError. +\begin{itemize} +\item {} +\member{action} (default: \code{"store"}) + +Determines \module{optparse}'s behaviour when this option is seen on the command +line; the available options are documented above. + +\item {} +\member{type} (default: \code{"string"}) + +The argument type expected by this option (e.g., \code{"string"} or +\code{"int"}); the available option types are documented below. + +\item {} +\member{dest} (default: derived from option strings) + +If the option's action implies writing or modifying a value somewhere, +this tells \module{optparse} where to write it: \member{dest} names an attribute of the +\code{options} object that \module{optparse} builds as it parses the command line. + +\item {} +\code{default} (deprecated) + +The value to use for this option's destination if the option is not +seen on the command line. Deprecated; use \code{parser.set{\_}defaults()} +instead. + +\item {} +\code{nargs} (default: 1) + +How many arguments of type \member{type} should be consumed when this +option is seen. If {\textgreater} 1, \module{optparse} will store a tuple of values to +\member{dest}. + +\item {} +\code{const} + +For actions that store a constant value, the constant value to store. + +\item {} +\code{choices} + +For options of type \code{"choice"}, the list of strings the user +may choose from. + +\item {} +\code{callback} + +For options with action \code{"callback"}, the callable to call when this +option is seen. See section~\ref{optparse-option-callbacks}, Option Callbacks for detail on the arguments +passed to \code{callable}. + +\item {} +\code{callback{\_}args}, \code{callback{\_}kwargs} + +Additional positional and keyword arguments to pass to \code{callback} +after the four standard callback arguments. + +\item {} +\member{help} + +Help text to print for this option when listing all available options +after the user supplies a \member{help} option (such as \code{"-{}-help"}). +If no help text is supplied, the option will be listed without help +text. To hide this option, use the special value \code{SUPPRESS{\_}HELP}. + +\item {} +\code{metavar} (default: derived from option strings) + +Stand-in for the option argument(s) to use when printing help text. +See section~\ref{optparse-tutorial}, the tutorial for an example. + +\end{itemize} + + \subsubsection{Standard option types\label{optparse-standard-option-types}} \module{optparse} has six built-in option types: \code{string}, \code{int}, \code{long}, @@ -1017,22 +1152,74 @@ Arguments to string options are not checked or converted in any way: the text on the command line is stored in the destination (or passed to the callback) as-is. -Integer arguments are passed to \code{int()} to convert them to Python -integers. If \code{int()} fails, so will \module{optparse}, although with a more -useful error message. (Internally, \module{optparse} raises -\exception{OptionValueError}; OptionParser catches this exception higher -up and terminates your program with a useful error message.) +Integer arguments (type \code{int} or \code{long}) are parsed as follows: +\begin{quote} +\begin{itemize} +\item {} +if the number starts with \code{0x}, it is parsed as a hexadecimal number + +\item {} +if the number starts with \code{0}, it is parsed as an octal number + +\item {} +if the number starts with \code{0b}, is is parsed as a binary number + +\item {} +otherwise, the number is parsed as a decimal number + +\end{itemize} +\end{quote} + +The conversion is done by calling either \code{int()} or \code{long()} with +the appropriate base (2, 8, 10, or 16). If this fails, so will \module{optparse}, +although with a more useful error message. -Likewise, \code{float} arguments are passed to \code{float()} for conversion, -\code{long} arguments to \code{long()}, and \code{complex} arguments to -\code{complex()}. Apart from that, they are handled identically to integer -arguments. +\code{float} and \code{complex} option arguments are converted directly with +\code{float()} and \code{complex()}, with similar error-handling. \code{choice} options are a subtype of \code{string} options. The \code{choices} option attribute (a sequence of strings) defines the set of allowed -option arguments. \code{optparse.option.check{\_}choice()} compares +option arguments. \code{optparse.check{\_}choice()} compares user-supplied option arguments against this master list and raises -\exception{OptionValueError} if an invalid string is given. +OptionValueError if an invalid string is given. + + +\subsubsection{Parsing arguments\label{optparse-parsing-arguments}} + +The whole point of creating and populating an OptionParser is to call +its \method{parse{\_}args()} method: +\begin{verbatim} +(options, args) = parser.parse_args(args=None, options=None) +\end{verbatim} + +where the input parameters are +\begin{description} +\item[\code{args}] +the list of arguments to process (\code{sys.argv{[}1:]} by default) +\item[\code{options}] +object to store option arguments in (a new instance of +optparse.Values by default) +\end{description} + +and the return values are +\begin{description} +\item[\code{options}] +the same object as was passed in as \code{options}, or the new +optparse.Values instance created by \module{optparse} +\item[\code{args}] +the leftover positional arguments after all options have been +processed +\end{description} + +The most common usage is to supply neither keyword argument. If you +supply a \code{values} object, it will be repeatedly modified with a +\code{setattr()} call for every option argument written to an option +destination, and finally returned by \method{parse{\_}args()}. + +If \method{parse{\_}args()} encounters any errors in the argument list, it calls +the OptionParser's \method{error()} method with an appropriate end-user error +message. This ultimately terminates your process with an exit status of +2 (the traditional \UNIX{} exit status for command-line errors). \subsubsection{Querying and manipulating your option parser\label{optparse-querying-manipulating-option-parser}} @@ -1050,9 +1237,8 @@ Returns the Option instance with the option string \code{opt{\_}str}, or If the OptionParser has an option corresponding to \code{opt{\_}str}, that option is removed. If that option provided any other option strings, all of those option strings become invalid. - If \code{opt{\_}str} does not occur in any option belonging to this -OptionParser, raises \exception{ValueError}. +OptionParser, raises ValueError. \end{description} @@ -1074,20 +1260,20 @@ options. If it finds any, it invokes the current conflict-handling mechanism. You can set the conflict-handling mechanism either in the constructor: \begin{verbatim} -parser = OptionParser(..., conflict_handler="...") +parser = OptionParser(..., conflict_handler=handler) \end{verbatim} or with a separate call: \begin{verbatim} -parser.set_conflict_handler("...") +parser.set_conflict_handler(handler) \end{verbatim} -The available conflict-handling mechanisms are: +The available conflict handlers are: \begin{quote} \begin{description} \item[\code{error} (default)] assume option conflicts are a programming error and raise -\exception{OptionConflictError} +OptionConflictError \item[\code{resolve}] resolve option conflicts intelligently (see below) \end{description} @@ -1131,7 +1317,78 @@ options: -n, --noisy be noisy --dry-run new dry-run option \end{verbatim} -% $Id: reference.txt 415 2004-09-30 02:26:17Z greg $ + + +\subsubsection{Cleanup\label{optparse-cleanup}} + +OptionParser instances have several cyclic references. This should not +be a problem for Python's garbage collector, but you may wish to break +the cyclic references explicitly by calling \code{destroy()} on your +OptionParser once you are done with it. This is particularly useful in +long-running applications where large object graphs are reachable from +your OptionParser. + + +\subsubsection{Other methods\label{optparse-other-methods}} + +OptionParser supports several other public methods: +\begin{itemize} +\item {} +\code{set{\_}usage(usage)} + +Set the usage string according to the rules described above for the +\code{usage} constructor keyword argument. Passing \code{None} sets the +default usage string; use \code{SUPPRESS{\_}USAGE} to suppress a usage +message. + +\item {} +\code{enable{\_}interspersed{\_}args()}, \code{disable{\_}interspersed{\_}args()} + +Enable/disable positional arguments interspersed with options, similar +to GNU getopt (enabled by default). For example, if \code{"-a"} and +\code{"-b"} are both simple options that take no arguments, \module{optparse} +normally accepts this syntax: +\begin{verbatim} +prog -a arg1 -b arg2 +\end{verbatim} + +and treats it as equivalent to +\begin{verbatim} +prog -a -b arg1 arg2 +\end{verbatim} + +To disable this feature, call \code{disable{\_}interspersed{\_}args()}. This +restores traditional \UNIX{} syntax, where option parsing stops with the +first non-option argument. + +\item {} +\code{set{\_}defaults(dest=value, ...)} + +Set default values for several option destinations at once. Using +\method{set{\_}defaults()} is the preferred way to set default values for +options, since multiple options can share the same destination. For +example, if several ``mode'' options all set the same destination, any +one of them can set the default, and the last one wins: +\begin{verbatim} +parser.add_option("--advanced", action="store_const", + dest="mode", const="advanced", + default="novice") # overridden below +parser.add_option("--novice", action="store_const", + dest="mode", const="novice", + default="advanced") # overrides above setting +\end{verbatim} + +To avoid this confusion, use \method{set{\_}defaults()}: +\begin{verbatim} +parser.set_defaults(mode="advanced") +parser.add_option("--advanced", action="store_const", + dest="mode", const="advanced") +parser.add_option("--novice", action="store_const", + dest="mode", const="novice") +\end{verbatim} + +\end{itemize} +% $Id: reference.txt 505 2005-07-22 01:52:40Z gward $ \subsection{Option Callbacks\label{optparse-option-callbacks}} @@ -1234,7 +1491,7 @@ its instance attributes: the current list of leftover arguments, ie. arguments that have been consumed but are neither options nor option arguments. Feel free to modify \code{parser.largs}, e.g. by adding more -arguments to it. (This list will become \var{args}, the second +arguments to it. (This list will become \code{args}, the second return value of \method{parse{\_}args()}.) \item[\code{parser.rargs}] the current list of remaining arguments, ie. with \code{opt{\_}str} and @@ -1260,7 +1517,7 @@ is a dictionary of arbitrary keyword arguments supplied via \subsubsection{Raising errors in a callback\label{optparse-raising-errors-in-callback}} -The callback function should raise \exception{OptionValueError} if there are any +The callback function should raise OptionValueError if there are any problems with the option or its argument(s). \module{optparse} catches this and terminates the program, printing the error message you supply to stderr. Your message should be clear, concise, accurate, and mention diff --git a/Lib/optparse.py b/Lib/optparse.py index ae3d00dc51..f4c2c702f4 100644 --- a/Lib/optparse.py +++ b/Lib/optparse.py @@ -16,7 +16,7 @@ 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.5a2" +__version__ = "1.5.1" __all__ = ['Option', 'SUPPRESS_HELP', @@ -35,8 +35,8 @@ __all__ = ['Option', 'BadOptionError'] __copyright__ = """ -Copyright (c) 2001-2004 Gregory P. Ward. All rights reserved. -Copyright (c) 2002-2004 Python Software Foundation. All rights reserved. +Copyright (c) 2001-2006 Gregory P. Ward. All rights reserved. +Copyright (c) 2002-2006 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 @@ -67,21 +67,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ import sys, os +import types import textwrap -try: - from gettext import gettext as _ -except ImportError: - _ = lambda arg: arg def _repr(self): return "<%s at 0x%x: %s>" % (self.__class__.__name__, id(self), self) # This file was generated from: -# Id: option_parser.py 421 2004-10-26 00:45:16Z greg -# Id: option.py 422 2004-10-26 00:53:47Z greg -# Id: help.py 367 2004-07-24 23:21:21Z gward -# Id: errors.py 367 2004-07-24 23:21:21Z gward +# Id: option_parser.py 509 2006-04-20 00:58:24Z gward +# Id: option.py 509 2006-04-20 00:58:24Z gward +# Id: help.py 509 2006-04-20 00:58:24Z gward +# Id: errors.py 509 2006-04-20 00:58:24Z gward + +try: + from gettext import gettext +except ImportError: + def gettext(message): + return message +_ = gettext + class OptParseError (Exception): def __init__(self, msg): @@ -120,8 +125,25 @@ class OptionValueError (OptParseError): class BadOptionError (OptParseError): """ - Raised if an invalid or ambiguous option is seen on the command-line. + Raised if an invalid option is seen on the command line. + """ + def __init__(self, opt_str): + self.opt_str = opt_str + + def __str__(self): + return _("no such option: %s") % self.opt_str + +class AmbiguousOptionError (BadOptionError): + """ + Raised if an ambiguous option is seen on the command line. """ + def __init__(self, opt_str, possibilities): + BadOptionError.__init__(self, opt_str) + self.possibilities = possibilities + + def __str__(self): + return (_("ambiguous option: %s (%s?)") + % (self.opt_str, ", ".join(self.possibilities))) class HelpFormatter: @@ -223,15 +245,30 @@ class HelpFormatter: def format_heading(self, heading): raise NotImplementedError, "subclasses must implement" - def format_description(self, description): - if not description: - return "" - desc_width = self.width - self.current_indent + def _format_text(self, text): + """ + Format a paragraph of free-form text for inclusion in the + help output at the current indentation level. + """ + text_width = self.width - self.current_indent indent = " "*self.current_indent - return textwrap.fill(description, - desc_width, + return textwrap.fill(text, + text_width, initial_indent=indent, - subsequent_indent=indent) + "\n" + subsequent_indent=indent) + + def format_description(self, description): + if description: + return self._format_text(description) + "\n" + else: + return "" + + def format_epilog(self, epilog): + if epilog: + return "\n" + self._format_text(epilog) + "\n" + else: + return "" + def expand_default(self, option): if self.parser is None or not self.default_tag: @@ -328,7 +365,7 @@ class IndentedHelpFormatter (HelpFormatter): self, indent_increment, max_help_position, width, short_first) def format_usage(self, usage): - return _("usage: %s\n") % usage + return _("Usage: %s\n") % usage def format_heading(self, heading): return "%*s%s:\n" % (self.current_indent, "", heading) @@ -353,8 +390,27 @@ class TitledHelpFormatter (HelpFormatter): return "%s\n%s\n" % (heading, "=-"[self.level] * len(heading)) -_builtin_cvt = { "int" : (int, _("integer")), - "long" : (long, _("long integer")), +def _parse_num(val, type): + if val[:2].lower() == "0x": # hexadecimal + radix = 16 + elif val[:2].lower() == "0b": # binary + radix = 2 + val = val[2:] or "0" # have to remove "0b" prefix + elif val[:1] == "0": # octal + radix = 8 + else: # decimal + radix = 10 + + return type(val, radix) + +def _parse_int(val): + return _parse_num(val, int) + +def _parse_long(val): + return _parse_num(val, long) + +_builtin_cvt = { "int" : (_parse_int, _("integer")), + "long" : (_parse_long, _("long integer")), "float" : (float, _("floating-point")), "complex" : (complex, _("complex")) } @@ -422,6 +478,7 @@ class Option: "store_true", "store_false", "append", + "append_const", "count", "callback", "help", @@ -435,6 +492,7 @@ class Option: "store_true", "store_false", "append", + "append_const", "count") # The set of actions for which it makes sense to supply a value @@ -448,6 +506,10 @@ class Option: ALWAYS_TYPED_ACTIONS = ("store", "append") + # The set of actions which take a 'const' attribute. + CONST_ACTIONS = ("store_const", + "append_const") + # The set of known types for option parsers. Again, listed here for # constructor argument validation. TYPES = ("string", "int", "long", "float", "complex", "choice") @@ -572,9 +634,17 @@ 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: + # Allow type objects or builtin type conversion functions + # (int, str, etc.) as an alternative to their names. (The + # complicated check of __builtin__ is only necessary for + # Python 2.1 and earlier, and is short-circuited by the + # first check on modern Pythons.) + import __builtin__ + if ( type(self.type) is types.TypeType or + (hasattr(self.type, "__name__") and + getattr(__builtin__, self.type.__name__, None) is self.type) ): self.type = self.type.__name__ + if self.type == "str": self.type = "string" @@ -589,7 +659,7 @@ class Option: if self.choices is None: raise OptionError( "must supply a list of choices for type 'choice'", self) - elif type(self.choices) not in (tuple, list): + elif type(self.choices) not in (types.TupleType, types.ListType): raise OptionError( "choices must be a list of strings ('%s' supplied)" % str(type(self.choices)).split("'")[1], self) @@ -613,7 +683,7 @@ class Option: self.dest = self._short_opts[0][1] def _check_const(self): - if self.action != "store_const" and self.const is not None: + if self.action not in self.CONST_ACTIONS and self.const is not None: raise OptionError( "'const' must not be supplied for action %r" % self.action, self) @@ -633,12 +703,12 @@ class Option: raise OptionError( "callback not callable: %r" % self.callback, self) if (self.callback_args is not None and - type(self.callback_args) is not tuple): + type(self.callback_args) is not types.TupleType): raise OptionError( "callback_args, if supplied, must be a tuple: not %r" % self.callback_args, self) if (self.callback_kwargs is not None and - type(self.callback_kwargs) is not dict): + type(self.callback_kwargs) is not types.DictType): raise OptionError( "callback_kwargs, if supplied, must be a dict: not %r" % self.callback_kwargs, self) @@ -720,6 +790,8 @@ class Option: setattr(values, dest, False) elif action == "append": values.ensure_value(dest, []).append(value) + elif action == "append_const": + values.ensure_value(dest, []).append(self.const) elif action == "count": setattr(values, dest, values.ensure_value(dest, 0) + 1) elif action == "callback": @@ -748,11 +820,9 @@ try: True, False except NameError: (True, False) = (1, 0) -try: - basestring -except NameError: - basestring = (str, unicode) +def isbasestring(x): + return isinstance(x, types.StringType) or isinstance(x, types.UnicodeType) class Values: @@ -766,16 +836,13 @@ class Values: __repr__ = _repr - def __eq__(self, other): + def __cmp__(self, other): if isinstance(other, Values): - return self.__dict__ == other.__dict__ - elif isinstance(other, dict): - return self.__dict__ == other + return cmp(self.__dict__, other.__dict__) + elif isinstance(other, types.DictType): + return cmp(self.__dict__, other) else: - return False - - def __ne__(self, other): - return not (self == other) + return -1 def _update_careful(self, dict): """ @@ -893,6 +960,13 @@ class OptionContainer: return self.description + def destroy(self): + """see OptionParser.destroy().""" + del self._short_opt + del self._long_opt + del self.defaults + + # -- Option-adding methods ----------------------------------------- def _check_conflict(self, option): @@ -926,7 +1000,7 @@ class OptionContainer: """add_option(Option) add_option(opt_str, ..., kwarg=val, ...) """ - if type(args[0]) is str: + if type(args[0]) is types.StringType: option = self.option_class(*args, **kwargs) elif len(args) == 1 and not kwargs: option = args[0] @@ -1018,6 +1092,11 @@ class OptionGroup (OptionContainer): def set_title(self, title): self.title = title + def destroy(self): + """see OptionParser.destroy().""" + OptionContainer.destroy(self) + del self.option_list + # -- Help-formatting methods --------------------------------------- def format_help(self, formatter): @@ -1044,6 +1123,8 @@ class OptionParser (OptionContainer): prog : string the name of the current program (to override os.path.basename(sys.argv[0])). + epilog : string + paragraph of help text to print after option help option_groups : [OptionGroup] list of option groups in this parser (option groups are @@ -1102,7 +1183,8 @@ class OptionParser (OptionContainer): description=None, formatter=None, add_help_option=True, - prog=None): + prog=None, + epilog=None): OptionContainer.__init__( self, option_class, conflict_handler, description) self.set_usage(usage) @@ -1114,6 +1196,7 @@ class OptionParser (OptionContainer): formatter = IndentedHelpFormatter() self.formatter = formatter self.formatter.set_parser(self) + self.epilog = epilog # Populate the option list; initial sources are the # standard_option_list class attribute, the 'option_list' @@ -1124,6 +1207,22 @@ class OptionParser (OptionContainer): self._init_parsing_state() + + def destroy(self): + """ + Declare that you are done with this OptionParser. This cleans up + reference cycles so the OptionParser (and all objects referenced by + it) can be garbage-collected promptly. After calling destroy(), the + OptionParser is unusable. + """ + OptionContainer.destroy(self) + for group in self.option_groups: + group.destroy() + del self.option_list + del self.option_groups + del self.formatter + + # -- Private methods ----------------------------------------------- # (used by our or OptionContainer's constructor) @@ -1167,7 +1266,7 @@ class OptionParser (OptionContainer): elif usage is SUPPRESS_USAGE: self.usage = None # For backwards compatibility with Optik 1.3 and earlier. - elif usage.startswith("usage:" + " "): + elif usage.lower().startswith("usage: "): self.usage = usage[7:] else: self.usage = usage @@ -1201,7 +1300,7 @@ class OptionParser (OptionContainer): defaults = self.defaults.copy() for option in self._get_all_options(): default = defaults.get(option.dest) - if isinstance(default, basestring): + if isbasestring(default): opt_str = option.get_opt_string() defaults[option.dest] = option.check_value(opt_str, default) @@ -1212,7 +1311,7 @@ class OptionParser (OptionContainer): def add_option_group(self, *args, **kwargs): # XXX lots of overlap with OptionContainer.add_option() - if type(args[0]) is str: + if type(args[0]) is types.StringType: group = OptionGroup(self, *args, **kwargs) elif len(args) == 1 and not kwargs: group = args[0] @@ -1276,7 +1375,7 @@ class OptionParser (OptionContainer): try: stop = self._process_args(largs, rargs, values) except (BadOptionError, OptionValueError), err: - self.error(err.msg) + self.error(str(err)) args = largs + rargs return self.check_values(values, args) @@ -1401,7 +1500,7 @@ class OptionParser (OptionContainer): i += 1 # we have consumed a character if not option: - self.error(_("no such option: %s") % opt) + raise BadOptionError(opt) if option.takes_value(): # Any characters left in arg? Pretend they're the # next arg, and stop consuming characters of arg. @@ -1501,7 +1600,7 @@ class OptionParser (OptionContainer): 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)) @@ -1513,6 +1612,9 @@ class OptionParser (OptionContainer): # Drop the last "\n", or the header if no options or option groups: return "".join(result[:-1]) + def format_epilog(self, formatter): + return formatter.format_epilog(self.epilog) + def format_help(self, formatter=None): if formatter is None: formatter = self.formatter @@ -1522,6 +1624,7 @@ class OptionParser (OptionContainer): if self.description: result.append(self.format_description(formatter) + "\n") result.append(self.format_option_help(formatter)) + result.append(self.format_epilog(formatter)) return "".join(result) def print_help(self, file=None): @@ -1555,11 +1658,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(s) else: # More than one possible completion: ambiguous prefix. - raise BadOptionError(_("ambiguous option: %s (%s?)") - % (s, ", ".join(possibilities))) + raise AmbiguousOptionError(s, possibilities) # Some day, there might be many Option classes. As of Optik 1.3, the diff --git a/Lib/test/test_optparse.py b/Lib/test/test_optparse.py index f656b9f3b9..991c06d447 100644 --- a/Lib/test/test_optparse.py +++ b/Lib/test/test_optparse.py @@ -10,17 +10,22 @@ import sys import os +import re import copy +import types import unittest from cStringIO import StringIO from pprint import pprint from test import test_support + from optparse import make_option, Option, IndentedHelpFormatter, \ TitledHelpFormatter, OptionParser, OptionContainer, OptionGroup, \ SUPPRESS_HELP, SUPPRESS_USAGE, OptionError, OptionConflictError, \ - BadOptionError, OptionValueError, Values, _match_abbrev + BadOptionError, OptionValueError, Values +from optparse import _match_abbrev +from optparse import _parse_num # Do the right thing with boolean values for all known Python versions. try: @@ -28,6 +33,7 @@ try: except NameError: (True, False) = (1, 0) +retype = type(re.compile('')) class InterceptedError(Exception): def __init__(self, @@ -96,7 +102,8 @@ Args were %(args)s.""" % locals ()) args -- positional arguments to `func` kwargs -- keyword arguments to `func` expected_exception -- exception that should be raised - expected_output -- output we expect to see + expected_message -- expected exception message (or pattern + if a compiled regex object) Returns the exception raised for further testing. """ @@ -109,14 +116,23 @@ Args were %(args)s.""" % locals ()) func(*args, **kwargs) except expected_exception, err: actual_message = str(err) - self.assertEqual(actual_message, - expected_message, + if isinstance(expected_message, retype): + self.assert_(expected_message.search(actual_message), """\ +expected exception message pattern: +/%s/ +actual exception message: +'''%s''' +""" % (expected_message.pattern, actual_message)) + else: + self.assertEqual(actual_message, + expected_message, + """\ expected exception message: -'''%(expected_message)s''' +'''%s''' actual exception message: -'''%(actual_message)s''' -""" % locals()) +'''%s''' +""" % (expected_message, actual_message)) return err else: @@ -157,7 +173,9 @@ and kwargs %(kwargs)r sys.stdout = save_stdout except InterceptedError, err: - self.assertEqual(output, expected_output) + if output != expected_output: + self.fail("expected: \n'''\n" + expected_output + + "'''\nbut got \n'''\n" + output + "'''") self.assertEqual(err.exit_status, expected_status) self.assertEqual(err.exit_message, expected_error) else: @@ -366,6 +384,23 @@ class TestOptionParser(BaseTest): self.assertRaises(self.parser.remove_option, ('foo',), None, ValueError, "no such option 'foo'") + def test_refleak(self): + # If an OptionParser is carrying around a reference to a large + # object, various cycles can prevent it from being GC'd in + # a timely fashion. destroy() breaks the cycles to ensure stuff + # can be cleaned up. + big_thing = [42] + refcount = sys.getrefcount(big_thing) + parser = OptionParser() + parser.add_option("-a", "--aaarggh") + parser.big_thing = big_thing + + parser.destroy() + #self.assertEqual(refcount, sys.getrefcount(big_thing)) + del parser + self.assertEqual(refcount, sys.getrefcount(big_thing)) + + class TestOptionValues(BaseTest): def setUp(self): pass @@ -391,13 +426,21 @@ class TestTypeAliases(BaseTest): def setUp(self): self.parser = OptionParser() - def test_type_aliases(self): - self.parser.add_option("-x", type=int) + def test_str_aliases_string(self): + self.parser.add_option("-s", type="str") + self.assertEquals(self.parser.get_option("-s").type, "string") + + def test_new_type_object(self): self.parser.add_option("-s", type=str) - self.parser.add_option("-t", type="str") + self.assertEquals(self.parser.get_option("-s").type, "string") + self.parser.add_option("-x", type=int) self.assertEquals(self.parser.get_option("-x").type, "int") + + def test_old_type_object(self): + self.parser.add_option("-s", type=types.StringType) self.assertEquals(self.parser.get_option("-s").type, "string") - self.assertEquals(self.parser.get_option("-t").type, "string") + self.parser.add_option("-x", type=types.IntType) + self.assertEquals(self.parser.get_option("-x").type, "int") # Custom type for testing processing of default values. @@ -487,13 +530,13 @@ class TestProgName(BaseTest): save_argv = sys.argv[:] try: sys.argv[0] = os.path.join("foo", "bar", "baz.py") - parser = OptionParser("usage: %prog ...", version="%prog 1.2") - expected_usage = "usage: baz.py ...\n" + parser = OptionParser("%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" + "Options:\n" " --version show program's version number and exit\n" " -h, --help show this help message and exit\n") finally: @@ -505,7 +548,7 @@ class TestProgName(BaseTest): usage="%prog arg arg") parser.remove_option("-h") parser.remove_option("--version") - expected_usage = "usage: thingy arg arg\n" + expected_usage = "Usage: thingy arg arg\n" self.assertUsage(parser, expected_usage) self.assertVersion(parser, "thingy 0.1") self.assertHelp(parser, expected_usage + "\n") @@ -515,9 +558,9 @@ class TestExpandDefaults(BaseTest): def setUp(self): self.parser = OptionParser(prog="test") self.help_prefix = """\ -usage: test [options] +Usage: test [options] -options: +Options: -h, --help show this help message and exit """ self.file_help = "read from FILE [default: %default]" @@ -699,13 +742,16 @@ class TestStandard(BaseTest): self.assertParseOK(["-a", "--", "foo", "bar"], {'a': "--", 'boo': None, 'foo': None}, ["foo", "bar"]), + self.assertParseOK(["-a", "--", "--foo", "bar"], + {'a': "--", 'boo': None, 'foo': ["bar"]}, + []), def test_short_option_joined_and_separator(self): self.assertParseOK(["-ab", "--", "--foo", "bar"], {'a': "b", 'boo': None, 'foo': None}, ["--foo", "bar"]), - def test_invalid_option_becomes_positional_arg(self): + def test_hyphen_becomes_positional_arg(self): self.assertParseOK(["-ab", "-", "--foo", "bar"], {'a': "b", 'boo': None, 'foo': ["bar"]}, ["-"]) @@ -870,6 +916,8 @@ class TestMultipleArgsAppend(BaseTest): type="float", dest="point") self.parser.add_option("-f", "--foo", action="append", nargs=2, type="int", dest="foo") + self.parser.add_option("-z", "--zero", action="append_const", + dest="foo", const=(0, 0)) def test_nargs_append(self): self.assertParseOK(["-f", "4", "-3", "blah", "--foo", "1", "666"], @@ -885,6 +933,11 @@ class TestMultipleArgsAppend(BaseTest): {'point': None, 'foo':[(3, 4)]}, []) + def test_nargs_append_const(self): + self.assertParseOK(["--zero", "--foo", "3", "4", "-z"], + {'point': None, 'foo':[(0, 0), (3, 4), (0, 0)]}, + []) + class TestVersion(BaseTest): def test_version(self): self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, @@ -960,8 +1013,14 @@ class TestExtendAddTypes(BaseTest): self.parser.add_option("-a", None, type="string", dest="a") self.parser.add_option("-f", "--file", type="file", dest="file") + def tearDown(self): + if os.path.isdir(test_support.TESTFN): + os.rmdir(test_support.TESTFN) + elif os.path.isfile(test_support.TESTFN): + os.unlink(test_support.TESTFN) + class MyOption (Option): - def check_file (option, opt, value): + def check_file(option, opt, value): if not os.path.exists(value): raise OptionValueError("%s: file does not exist" % value) elif not os.path.isfile(value): @@ -972,25 +1031,23 @@ class TestExtendAddTypes(BaseTest): TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER) TYPE_CHECKER["file"] = check_file - def test_extend_file(self): + def test_filetype_ok(self): open(test_support.TESTFN, "w").close() self.assertParseOK(["--file", test_support.TESTFN, "-afoo"], {'file': test_support.TESTFN, 'a': 'foo'}, []) - os.unlink(test_support.TESTFN) - - def test_extend_file_nonexistent(self): + def test_filetype_noexist(self): self.assertParseFail(["--file", test_support.TESTFN, "-afoo"], "%s: file does not exist" % test_support.TESTFN) - def test_file_irregular(self): + def test_filetype_notfile(self): os.mkdir(test_support.TESTFN) self.assertParseFail(["--file", test_support.TESTFN, "-afoo"], "%s: not a regular file" % test_support.TESTFN) - os.rmdir(test_support.TESTFN) + class TestExtendAddActions(BaseTest): def setUp(self): @@ -1003,7 +1060,7 @@ class TestExtendAddActions(BaseTest): STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",) TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",) - def take_action (self, action, dest, opt, value, values, parser): + def take_action(self, action, dest, opt, value, values, parser): if action == "extend": lvalue = value.split(",") values.ensure_value(dest, []).extend(lvalue) @@ -1072,7 +1129,7 @@ class TestCallback(BaseTest): callback=lambda: None, type="string", help="foo") - expected_help = ("options:\n" + expected_help = ("Options:\n" " -t TEST, --test=TEST foo\n") self.assertHelp(parser, expected_help) @@ -1085,7 +1142,7 @@ class TestCallbackExtraArgs(BaseTest): dest="points", default=[])] self.parser = OptionParser(option_list=options) - def process_tuple (self, option, opt, value, parser_, len, type): + def process_tuple(self, option, opt, value, parser_, len, type): self.assertEqual(len, 3) self.assert_(type is int) @@ -1110,7 +1167,7 @@ class TestCallbackMeddleArgs(BaseTest): self.parser = OptionParser(option_list=options) # Callback that meddles in rargs, largs - def process_n (self, option, opt, value, parser_): + def process_n(self, option, opt, value, parser_): # option is -3, -5, etc. nargs = int(opt[1:]) rargs = parser_.rargs @@ -1139,7 +1196,7 @@ class TestCallbackManyArgs(BaseTest): callback=self.process_many, type="int")] self.parser = OptionParser(option_list=options) - def process_many (self, option, opt, value, parser_): + def process_many(self, option, opt, value, parser_): if opt == "-a": self.assertEqual(value, ("foo", "bar")) elif opt == "--apple": @@ -1162,7 +1219,7 @@ class TestCallbackCheckAbbrev(BaseTest): self.parser.add_option("--foo-bar", action="callback", callback=self.check_abbrev) - def check_abbrev (self, option, opt, value, parser): + def check_abbrev(self, option, opt, value, parser): self.assertEqual(opt, "--foo-bar") def test_abbrev_callback_expansion(self): @@ -1177,7 +1234,7 @@ class TestCallbackVarArgs(BaseTest): self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, option_list=options) - def variable_args (self, option, opt, value, parser): + def variable_args(self, option, opt, value, parser): self.assert_(value is None) done = 0 value = [] @@ -1229,7 +1286,7 @@ class ConflictBase(BaseTest): self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, option_list=options) - def show_version (self, option, opt, value, parser): + def show_version(self, option, opt, value, parser): parser.values.show_version = 1 class TestConflict(ConflictBase): @@ -1280,7 +1337,7 @@ class TestConflictResolve(ConflictBase): def test_conflict_resolve_help(self): self.assertOutput(["-h"], """\ -options: +Options: --verbose increment verbosity -h, --help show this help message and exit -v, --version show version @@ -1319,7 +1376,7 @@ class TestConflictOverride(BaseTest): def test_conflict_override_help(self): self.assertOutput(["-h"], """\ -options: +Options: -h, --help show this help message and exit -n, --dry-run dry run mode """) @@ -1332,9 +1389,9 @@ options: # -- Other testing. ---------------------------------------------------- _expected_help_basic = """\ -usage: bar.py [options] +Usage: bar.py [options] -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) @@ -1343,9 +1400,9 @@ options: """ _expected_help_long_opts_first = """\ -usage: bar.py [options] +Usage: bar.py [options] -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) @@ -1358,7 +1415,7 @@ Usage ===== bar.py [options] -options +Options ======= -a APPLE throw APPLEs at basket --boo=NUM, -b NUM shout "boo!" NUM times (in order to frighten away all the @@ -1368,9 +1425,9 @@ options """ _expected_help_short_lines = """\ -usage: bar.py [options] +Usage: bar.py [options] -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 @@ -1382,15 +1439,8 @@ options: class TestHelp(BaseTest): def setUp(self): - self.orig_columns = os.environ.get('COLUMNS') self.parser = self.make_parser(80) - def tearDown(self): - if self.orig_columns is None: - del os.environ['COLUMNS'] - else: - os.environ['COLUMNS'] = self.orig_columns - def make_parser(self, columns): options = [ make_option("-a", type="string", dest='a', @@ -1419,7 +1469,7 @@ class TestHelp(BaseTest): self.assertHelpEquals(_expected_help_basic) def test_help_old_usage(self): - self.parser.set_usage("usage: %prog [options]") + self.parser.set_usage("Usage: %prog [options]") self.assertHelpEquals(_expected_help_basic) def test_help_long_opts_first(self): @@ -1449,13 +1499,13 @@ class TestHelp(BaseTest): group.add_option("-g", action="store_true", help="Group option.") self.parser.add_option_group(group) - self.assertHelpEquals("""\ -usage: bar.py [options] + expect = """\ +Usage: bar.py [options] This is the program description for bar.py. bar.py has an option group as well as single options. -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) @@ -1467,9 +1517,12 @@ options: that some of them bite. -g Group option. -""") +""" + self.assertHelpEquals(expect) + self.parser.epilog = "Please report bugs to /dev/null." + self.assertHelpEquals(expect + "\nPlease report bugs to /dev/null.\n") class TestMatchAbbrev(BaseTest): @@ -1490,6 +1543,43 @@ class TestMatchAbbrev(BaseTest): BadOptionError, "ambiguous option: --f (%s?)" % possibilities) +class TestParseNumber(BaseTest): + def setUp(self): + self.parser = InterceptingOptionParser() + self.parser.add_option("-n", type=int) + self.parser.add_option("-l", type=long) + + def test_parse_num_fail(self): + self.assertRaises( + _parse_num, ("", int), {}, + ValueError, + re.compile(r"invalid literal for int().*: '?'?")) + self.assertRaises( + _parse_num, ("0xOoops", long), {}, + ValueError, + re.compile(r"invalid literal for long().*: '?0xOoops'?")) + + def test_parse_num_ok(self): + self.assertEqual(_parse_num("0", int), 0) + self.assertEqual(_parse_num("0x10", int), 16) + self.assertEqual(_parse_num("0XA", long), 10L) + self.assertEqual(_parse_num("010", long), 8L) + self.assertEqual(_parse_num("0b11", int), 3) + self.assertEqual(_parse_num("0b", long), 0L) + + def test_numeric_options(self): + self.assertParseOK(["-n", "42", "-l", "0x20"], + { "n": 42, "l": 0x20 }, []) + self.assertParseOK(["-n", "0b0101", "-l010"], + { "n": 5, "l": 8 }, []) + self.assertParseFail(["-n008"], + "option -n: invalid integer value: '008'") + self.assertParseFail(["-l0b0123"], + "option -l: invalid long integer value: '0b0123'") + self.assertParseFail(["-l", "0x12x"], + "option -l: invalid long integer value: '0x12x'") + + def _testclasses(): mod = sys.modules[__name__] return [getattr(mod, name) for name in dir(mod) if name.startswith('Test')] diff --git a/Misc/NEWS b/Misc/NEWS index 8813d7f885..03099d1e57 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -86,6 +86,11 @@ Extension Modules Library ------- +- Updated optparse module to Optik 1.5.1 (allow numeric constants in + hex, octal, or binary; add ``append_const`` action; keep going if + gettext cannot be imported; added ``OptionParser.destroy()`` method; + added ``epilog`` for better help generation). + - Bug #1473760: ``tempfile.TemporaryFile()`` could hang on Windows, when called from a thread spawned as a side effect of importing a module. -- 2.40.0