When argparse will be in the python standard installation
Steven Bethard
steven.bethard at gmail.com
Fri Jan 5 13:21:46 EST 2007
[Thanks for looking through all these Martin!]
Martin v. Löwis wrote:
> Steven Bethard schrieb:
>> * alias ArgumentParser to OptionParser
>> * alias add_argument to add_option
>> * alias Values to Namespace
>> * alias OptionError and OptionValueError to ArgumentError
>> * alias add_help= keyword argument of ArgumentParser to add_help_option=
>> * alias namespace= keyword argument of parse_args to options=
>
> Not sure what you mean by "alias": I don't think the argparse names
> should be retained (unless you feel they describe things better).
They do describe things a bit better. Given a command line like::
prog --opt1 o --opt2 pos1 pos2 pos3
optparse can only parse out the optional command line arguments,
``--opt1 o --opt2``, hence it uses names like ``add_option``. On the
other hand, argparse can parse the entire command line (including the
positional arguments ``pos1 pos2 pos3``) so it uses ``add_argument``
instead of ``add_option``.
>> * ArgumentParser.parse_args returns a single namespace object, not an
>> (options, args) tuple, since argparse handles positional arguments.
>> This could probably be addressed by adding an __iter__ method to the
>> Namespace object which would return (self, [])
>
> I find that I'm not that familiar with command line arguments :-)
>
> To understand that difference, I would have to see examples of both,
> and why it is better to return a single result.
A brief example to illustrate the differences. Here is a program that
reads the command-line args and writes the sum to a log file specified
by --log. With optparse, this would look something like::
parser = optparse.OptionParser()
parser.add_option('--log')
options, args = parser.parse_args()
if options.log is not None:
logfile = open(options.log)
else:
logfile = sys.stdout
try:
integers = [int(arg) for arg in args]
except ValueError:
parser.error('positional arguments must be integers')
logfile.write('%s\n' % sum(integers))
logfile.close()
Since argparse handles positional arguments as well as optional
arguments (and since it accepts type='outfile') the argparse version is
much more concise::
parser = argparse.ArgumentParser()
parser.add_argument('integers', type=int, nargs='+')
parser.add_argument('--log', type='outfile', default=sys.stdout)
args = parser.parse_args()
args.log.write('%s\n' % sum(args.integers))
args.log.close()
So basically, since argparse parses the entire command line (instead of
just the optional parts) there is no concept of "remaining command line
arguments" -- *all* command line arguments have become attributes of the
namespace returned by parse_args(). Thus, while optparse has to return
both a namespace and the unparsed portion of the command line, argparse
only has to return the namespace.
>> * argparse makes the default value for a "store_true" action False, and
>> the default value for a "store_false" action True. This is what users
>> expect, but different from optparse where the default is always None.
>
> Could we deprecate "store_false" with no default?
That's probably reasonable. In Python 3.0, the default could then
become the appropriate one.
> It may be that users expect to see True and False, but how can you find
> out whether the option was given or not afterwards?
For both optparse and argparse, with "store_false", you know the option
was not given if the stored value is something other than ``False``, and
with "store_true", you know the option was not given if the stored value
is something other than ``True``.
With optparse, that "something other" is always ``None`` which is
unfortunate because it makes the following code fail::
parser = optparse.OptionParser()
parser.add_option('--not-foo', action='store_false')
options, args = parser.parse_args()
if options.not_foo:
# do something
The ``do something`` block will execute every time because
``options.not_foo`` is either ``False`` or ``None``, both of which
indicate that the if-statement should not be executed.
In argparse, the code above works fine because the default is ``True``
instead of ``None``.
> Alternatively, could the actions become keyword arguments (with the
> action argument only preserved for backwards compatibility)? E.g.
>
> parser.add_option("-v", store_true_into="verbose")
> parser.add_option("-q", store_false_into="verbose")
> (same for store, append, count, callback)
It looks like you're trying to merge the ``dest=`` and ``action=``
functionality, but I'm not sure I understand where this is going...
>> * the choices argument is now checked *after* arguments have been
>> type-converted. This is intentional, so that you can specify, say
>> xrange(100) instead of ["0", "1", "2", "3", ... "99"]. There is also
>> no "choices" type anymore since any action can also specify their
>> choices.
>
> As an incompatible change, this could be warned-about if the type is not
> string, yet the first choice is.
>
> Alternatively, we could require users that pass converted values to
> specify converted_choices=True in 2.6, and warn all users that don't
> pass a converted_choices flag, and then flip the default in 2.7 and 3.0.
> Then current users had a quick fix to silence the warning, and new
> users could drop the extra flag in the future.
That seems reasonable.
>> I could probably add callback actions by creating an appropriate
>> Action subclass and registering it. However, any code relying on
>> parser.{largs,rargs,values} would break because the parsing algorithm
>> is completely different in argparse.
>
> If you can find a way to make callbacks work in the "common case",
> this flag could be deprecated and removed.
Sounds good.
>> I guess I don't know how to proceed from here. I'm reluctant to start
>> adding the code necessary to support even the easily solved issues when
>> the issues that I don't know how to solve seem like they could be deal
>> breakers.
>
> This asks for a PEP. The views above are mine only, others may feel
> differently.
Ok. I'll try to write a PEP up this week.
Thanks again for all your useful feedback!
STeVe
More information about the Python-list
mailing list