diff -r 46cadd3955d0 Doc/library/argparse.rst
--- a/Doc/library/argparse.rst Sat Mar 16 09:15:47 2013 -0700
+++ b/Doc/library/argparse.rst Fri Mar 22 10:09:55 2013 -0700
@@ -168,6 +168,9 @@
* usage_ - The string describing the program usage (default: generated)
+ * args_default_to_positional_ - Parse unrecognized arguments as
+ positionals, as opposed to optionals (default: False).
+
The following sections describe how each of these are used.
@@ -602,6 +605,36 @@
The ``%(prog)s`` format specifier is available to fill in the program name in
your usage messages.
+args_default_to_positional
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+:class:`ArgumentParser` parses the argument strings, classifying them as
+optionals or positionals (arguments). By default, a string starting with one of the
+``prefix_chars`` is assumed to be a command-line option. If ``args_default_to_positional=True``,
+such a string will be parsed as an argument, unless it matches one of the defined
+command-line options::
+
+ >>> parser = argparse.ArgumentParser(args_default_to_positional=True)
+ >>> parser.add_argument('--foo')
+ >>> parser.add_argument('--bar', nargs=2)
+ >>> parser.parse_args('--foo --baz --bar -one -two'.split())
+ Namespace(bar=['-one', '-two'], foo='--baz')
+
+This approximates the behavior of :mod:`optparse`, which freely accepts
+arguments that begin with ``'-'``. Note however that in this example ``--foo --bar``
+gives an error, since ``--bar`` is defined as a command-line option::
+
+ >>> parser.parse_args('--foo --bar'.split())
+ usage: [-h] [--foo FOO] [--bar BAR BAR]
+ : error: argument --foo: expected one argument
+
+Joining the option and value with `=` gets around this limitation::
+
+ >>> parser.parse_args('--foo=--bar'.split())
+ Namespace(bar=None, foo='--bar')
+
+See also `Arguments containing -`_.
+
The add_argument() method
-------------------------
@@ -1320,6 +1353,7 @@
usage: PROG [-h] [--foo FOO] [bar]
PROG: error: extra arguments found: badger
+.. _`Arguments containing -`:
Arguments containing ``-``
^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -1344,6 +1378,10 @@
>>> parser.parse_args(['-x', '-1', '-5'])
Namespace(foo='-5', x='-1')
+ >>> # complex and scientific notation are accepted as well
+ >>> parser.parse_args(['-x', '-1e-3', '-1-4j'])
+ Namespace(foo='-1-4j', x='-1e-3')
+
>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('-1', dest='one')
>>> parser.add_argument('foo', nargs='?')
@@ -1352,16 +1390,20 @@
>>> parser.parse_args(['-1', 'X'])
Namespace(foo=None, one='X')
- >>> # negative number options present, so -2 is an option
+ >>> # negative number options present, so -2 is an unrecognized option
>>> parser.parse_args(['-2'])
usage: PROG [-h] [-1 ONE] [foo]
- PROG: error: no such option: -2
+ PROG: error: unrecognized arguments: -2
>>> # negative number options present, so both -1s are options
>>> parser.parse_args(['-1', '-1'])
usage: PROG [-h] [-1 ONE] [foo]
PROG: error: argument -1: expected one argument
+ >>> # negative number options present, '-1...' is -1 option plus argument
+ >>> parser.test(['-1.3e4'])
+ Namespace(foo=None, one='.3e4')
+
If you have positional arguments that must begin with ``-`` and don't look
like negative numbers, you can insert the pseudo-argument ``'--'`` which tells
:meth:`~ArgumentParser.parse_args` that everything after that is a positional
@@ -1370,6 +1412,23 @@
>>> parser.parse_args(['--', '-f'])
Namespace(foo='-f', one=None)
+:class:`ArgumentParser` optional argument, args_default_to_positional_
+alters this behavior, allowing a positional argument to begin with ``-``,
+provided it does not match a defined option. Use this alternative with caution
+if positional arguments could be confused with command options::
+
+ >>> parser = argparse.ArgumentParser(args_default_to_positional=True)
+ >>> parser.add_argument('-1', dest='one')
+ >>> parser.add_argument('foo', nargs='?')
+
+ >>> # default_to_positional, '-2-4j' is now recognized as an argument
+ >>> parser.parse_args(['-1', '-2-4j', '-3-4j'])
+ Namespace(foo='-3-4j', one='-2-4j')
+
+ >>> # default_to_positional, '-1.3e4' still matches the -1 option
+ >>> parser.parse_args(['-1.3e4'])
+ Namespace(foo=None, one='.3e4')
+
Argument abbreviations
^^^^^^^^^^^^^^^^^^^^^^
diff -r 46cadd3955d0 Lib/argparse.py
--- a/Lib/argparse.py Sat Mar 16 09:15:47 2013 -0700
+++ b/Lib/argparse.py Fri Mar 22 10:09:55 2013 -0700
@@ -1594,7 +1594,9 @@
fromfile_prefix_chars=None,
argument_default=None,
conflict_handler='error',
- add_help=True):
+ add_help=True,
+ args_default_to_positional=False,
+ ):
superinit = super(ArgumentParser, self).__init__
superinit(description=description,
@@ -1612,6 +1614,7 @@
self.formatter_class = formatter_class
self.fromfile_prefix_chars = fromfile_prefix_chars
self.add_help = add_help
+ self.args_default_to_positional = args_default_to_positional
add_group = self.add_argument_group
self._positionals = add_group(_('positional arguments'))
@@ -2107,12 +2110,20 @@
option_tuple, = option_tuples
return option_tuple
- # if it was not found as an option, but it looks like a negative
- # number, it was meant to be positional
+ # behave more like optparse even if the argument looks like a option
+ if self.args_default_to_positional:
+ return None
+
+ # if it is not found as an option, but looks like a number
+ # it is meant to be positional
# unless there are negative-number-like options
- if self._negative_number_matcher.match(arg_string):
- if not self._has_negative_number_optionals:
+ # try complex() is more general than self._negative_number_matcher
+ if not self._has_negative_number_optionals:
+ try:
+ complex(arg_string)
return None
+ except ValueError:
+ pass
# if it contains a space, it was meant to be a positional
if ' ' in arg_string:
diff -r 46cadd3955d0 Lib/test/test_argparse.py
--- a/Lib/test/test_argparse.py Sat Mar 16 09:15:47 2013 -0700
+++ b/Lib/test/test_argparse.py Fri Mar 22 10:09:55 2013 -0700
@@ -1266,7 +1266,8 @@
failures = ['-x', '-y2.5', '-xa', '-x -a',
'-x -3', '-x -3.5', '-3 -3.5',
'-x -2.5', '-x -2.5 a', '-3 -.5',
- 'a x -1', '-x -1 a', '-3 -1 a']
+ 'a x -1', '-x -1 a', '-3 -1 a',
+ '-1E3']
successes = [
('', NS(x=None, y=None, z=[])),
('-x 2.5', NS(x=2.5, y=None, z=[])),
@@ -1279,8 +1280,176 @@
('a -x 1', NS(x=1.0, y=None, z=['a'])),
('-x 1 a', NS(x=1.0, y=None, z=['a'])),
('-3 1 a', NS(x=None, y=1.0, z=['a'])),
+ # scientific notation should work
+ ('-3-.5e1', NS(x=None, y=-5.0, z=[])),
+ ('-x-0.5e1', NS(x=-5.0, y=None, z=[])),
]
+class TestScientificComplex(ParserTestCase):
+ """Tests scientific notation and complex"""
+
+ argument_signatures = [
+ Sig('--xdiv', nargs=2, type=float, default=[0,1]),
+ Sig('--zdiv', nargs=2, type=complex, default=argparse.SUPPRESS)
+ ]
+ failures = []
+ successes = [
+ ('', NS(xdiv=[0,1])),
+ ('--xdiv -.5 .5', NS(xdiv=[-0.5, 0.5])),
+ ('--xdiv -5e-1 5e-1', NS(xdiv=[-0.5, 0.5])),
+ ('--zdiv -1e-1-1j 5e1+3j', NS(xdiv=[0,1], zdiv=[(-0.1-1j), (50+3j)])),
+ ]
+
+class TestDefaultToPositional(ParserTestCase):
+ """Tests args_default_to_positional option"""
+
+ parser_signature = Sig(args_default_to_positional=True)
+ argument_signatures = [
+ Sig('--foo', nargs=2),
+ Sig('-a','--asciidoc-opts')
+ ]
+ failures = ['--asciidoc-opts --foo','--asciidoc-opts=--foo --foo - -a',
+ ]
+ successes = [
+ ('', NS(foo=None, asciidoc_opts=None)),
+ ('--foo one two', NS(foo=['one','two'], asciidoc_opts=None)),
+ ('--foo -one two', NS(foo=['-one','two'], asciidoc_opts=None)),
+ ('--asciidoc-opts --safe', NS(asciidoc_opts='--safe', foo=None)),
+ ('--asciidoc-opts=--safe', NS(asciidoc_opts='--safe', foo=None)),
+ ('-a--safe', NS(asciidoc_opts='--safe', foo=None)),
+ ('--asciidoc-opts=--foo --foo - -f', NS(asciidoc_opts='--foo', foo=['-', '-f'])),
+ ]
+
+class TestDefaultToPositionalWithOptionLike(ParserTestCase):
+ """Tests args_default_to_positional option with number-like options
+ compare to TestOptionLike"""
+
+ parser_signature = Sig(args_default_to_positional=True)
+
+ argument_signatures = [
+ Sig('-x', type=float),
+ Sig('-3', type=float, dest='y'),
+ Sig('z', nargs='*'),
+ ]
+ failures = [
+ '-x',
+ '-xa',
+ '-x -a',
+ '-x -3',
+ '-x -3.5',
+ '-3 -3.5',
+ ]
+ successes = [
+ # TestOptionLike successes remain successes
+ ('', NS(x=None, y=None, z=[])),
+ ('-x 2.5', NS(x=2.5, y=None, z=[])),
+ ('-x 2.5 a', NS(x=2.5, y=None, z=['a'])),
+ ('-3.5', NS(x=None, y=0.5, z=[])),
+ ('-3-.5', NS(x=None, y=-0.5, z=[])),
+ ('-3 .5', NS(x=None, y=0.5, z=[])),
+ ('a -3.5', NS(x=None, y=0.5, z=['a'])),
+ ('a', NS(x=None, y=None, z=['a'])),
+ ('a -x 1', NS(x=1.0, y=None, z=['a'])),
+ ('-x 1 a', NS(x=1.0, y=None, z=['a'])),
+ ('-3 1 a', NS(x=None, y=1.0, z=['a'])),
+ ('-3-.5e1', NS(x=None, y=-5.0, z=[])),
+ ('-x-0.5e1', NS(x=-5.0, y=None, z=[])),
+ # failure cases that have become successes
+ ('-y2.5', NS(x=None, y=None, z=['-y2.5'])),
+ ('-x -2.5', NS(x=-2.5, y=None, z=[])),
+ ('-x -2.5 a', NS(x=-2.5, y=None, z=['a'])),
+ ('-3 -.5', NS(x=None, y=-0.5, z=[])),
+ ('a x -1', NS(x=None, y=None, z=['a', 'x', '-1'])),
+ ('-x -1 a', NS(x=-1, y=None, z=['a'])),
+ ('-3 -1 a', NS(x=None, y=-1, z=['a'])),
+ ('-1E3', NS(x=None, y=None, z=['-1E3'])),
+ ]
+
+class TestOptparseLike(ParserTestCase):
+ """optparse TestStandard cases, without positionals"""
+ parser_signature = Sig()
+ # value of args_default_to_positional does not matter
+ argument_signatures = [
+ Sig("-a"),
+ Sig("-b", "--boo", type=int, dest='boo'),
+ Sig("--foo", action="append"),
+ ]
+ failures = ["-a", "-b 5x","--boo13","--boo=x5"]
+ successes = [
+ ('', NS(**{'a': None, 'boo': None, 'foo': None})),
+ # test_shortopt_empty_longopt_append
+ (["-a", "", "--foo=blah", "--foo="], #"-a --foo=blah --foo=",
+ NS(**{'a': "", 'boo': None, 'foo': ["blah", ""]})),
+ # test_shortopt_empty_longopt_append
+ (["--foo", "bar", "--foo", "", "--foo=x"],
+ NS(**{'a': None, 'boo': None, 'foo': ["bar", "", "x"]})),
+ # test_long_option_append
+ (["--foo", "bar", "--foo", "", "--foo=x"],
+ NS(**{'a': None, 'boo': None, 'foo': ["bar", "", "x"]})),
+ # test_option_argument_joined
+ (["-abc"], NS(**{'a': "bc", 'boo': None, 'foo': None})),
+ # test_option_argument_split
+ (["-a", "34"], NS(**{'a': "34", 'boo': None, 'foo': None})),
+ # test_option_argument_joined_integer
+ (["-b34"], NS(**{'a': None, 'boo': 34, 'foo': None})),
+ # test_option_argument_split_negative_integer
+ (["-b", "-5"], NS(**{'a': None, 'boo': -5, 'foo': None})),
+ # test_long_option_argument_joined
+ (["--boo=13"], NS(**{'a': None, 'boo': 13, 'foo': None})),
+ # test_long_option_argument_split
+ (["--boo", "111"], NS(**{'a': None, 'boo': 111, 'foo': None})),
+ # test_long_option_short_option
+ (["--foo=bar", "-axyz"],
+ NS(**{'a': 'xyz', 'boo': None, 'foo': ["bar"]})),
+ # test_abbrev_long_option
+ (["--f=bar", "-axyz"],
+ NS(**{'a': 'xyz', 'boo': None, 'foo': ["bar"]})),
+ # test_short_and_long_option_split
+ (["-a", "xyz", "--foo", "bar"],
+ NS(**{'a': 'xyz', 'boo': None, 'foo': ["bar"]})),
+ # test_short_option_split_long_option_append
+ (["--foo=bar", "-b", "123", "--foo", "baz"],
+ NS(**{'a': None, 'boo': 123, 'foo': ["bar", "baz"]})),
+ ]
+
+class TestOptparseWithPositionalLike(ParserTestCase):
+ """optparse TestStandard cases, with positionals"""
+ # value of args_default_to_positional does not matter
+ parser_signature = Sig()
+ argument_signatures = [
+ Sig("-a"),
+ Sig("-b", "--boo", type=int, dest='boo'),
+ Sig("--foo", action="append"),
+ Sig("pos", nargs='*', default=argparse.SUPPRESS)
+ ]
+ failures = [
+ # test_short_option_consumes_separator
+ # even with default_to_positionals, '--' is still a separator
+ # (["-a", "--", "foo", "bar"],
+ # NS(**{'a': "--", 'boo': None, 'foo': None, 'pos': ["foo", "bar"]})),
+ '-a -- foo bar',
+ # (["-a", "--", "--foo", "bar"],
+ # NS(**{'a': "--", 'boo': None, 'foo': ["bar"]})),
+ '-a -- --foo bar',
+ # test_option_consumes_optionlike_string
+ # even with default_to_positionals, -b is not an argument
+ # (["-a", "-b3"], NS(**{'a': "-b3", 'boo': None, 'foo': None})),
+ '-a -b3'
+ ]
+ successes = [
+ # test_short_option_split_one_positional_arg
+ (["-a", "foo", "bar"],
+ NS(**{'a': "foo", 'boo': None, 'foo': None, 'pos': ["bar"]})),
+ # test_short_option_joined_and_separator
+ (["-ab", "--", "--foo", "bar"],
+ NS(**{'a': "b", 'boo': None, 'foo': None, 'pos': ["--foo", "bar"]})),
+ # test_hyphen_becomes_positional_arg
+ (["-ab", "-", "--foo", "bar"],
+ NS(**{'a': "b", 'boo': None, 'foo': ["bar"], 'pos': ["-"]})),
+ # test_no_append_versus_append
+ (["-b3", "-b", "5", "--foo=bar", "--foo", "baz"],
+ NS(**{'a': None, 'boo': 5, 'foo': ["bar", "baz"]})),
+ ]
class TestDefaultSuppress(ParserTestCase):
"""Test actions with suppressed defaults"""
diff -r 46cadd3955d0 Misc/ACKS
--- a/Misc/ACKS Sat Mar 16 09:15:47 2013 -0700
+++ b/Misc/ACKS Fri Mar 22 10:09:55 2013 -0700
@@ -6,7 +6,7 @@
bug reports, ideas, moral support, endorsement, or even complaints....
Without you, I would've stopped working on Python long ago!
- --Guido
+ --Guido
PS: In the standard Python distribution, this file is encoded in UTF-8
and the list is in rough alphabetical order by last names.
@@ -562,6 +562,7 @@
Bob Ippolito
Roger Irwin
Atsuo Ishimoto
+Paul Jacobson
Adam Jackson
Ben Jackson
Paul Jackson