Skip to content

Commit c2c4dfa

Browse files
committed
gh-53584: Add reproducers for greedy opts issue
Most of these are marked as expected fail for now, pending the fix. Signed-off-by: Stephen Finucane <stephen@that.guru>
1 parent 0e3b3b8 commit c2c4dfa

File tree

1 file changed

+108
-0
lines changed

1 file changed

+108
-0
lines changed

Lib/test/test_argparse.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,17 @@ class TestOptionalsNargs3(ParserTestCase):
664664
('-x a b c', NS(x=['a', 'b', 'c'])),
665665
]
666666

667+
def test_does_not_steal_required_positional(self):
668+
# https://github.com/python/cpython/issues/53584
669+
# Fixed integer nargs is never greedy: --foo takes exactly N args and
670+
# leaves the remainder for positionals.
671+
parser = ErrorRaisingArgumentParser()
672+
parser.add_argument('--foo', nargs=2)
673+
parser.add_argument('bar')
674+
args = parser.parse_args(['--foo', 'a', 'b', 'c'])
675+
self.assertEqual(args.foo, ['a', 'b'])
676+
self.assertEqual(args.bar, 'c')
677+
667678

668679
class TestOptionalsNargsOptional(ParserTestCase):
669680
"""Tests specifying an Optional arg for an Optional"""
@@ -687,6 +698,51 @@ class TestOptionalsNargsOptional(ParserTestCase):
687698
('-z 2', NS(w=None, x=None, y='spam', z=2)),
688699
]
689700

701+
@unittest.expectedFailure
702+
def test_does_not_steal_required_positional(self):
703+
# https://github.com/python/cpython/issues/53584
704+
parser = ErrorRaisingArgumentParser()
705+
parser.add_argument('--foo', nargs='?')
706+
parser.add_argument('bar')
707+
args = parser.parse_args(['--foo', 'abc'])
708+
self.assertIsNone(args.foo)
709+
self.assertEqual(args.bar, 'abc')
710+
711+
@unittest.expectedFailure
712+
def test_does_not_steal_two_required_positionals(self):
713+
# https://github.com/python/cpython/issues/53584
714+
parser = ErrorRaisingArgumentParser()
715+
parser.add_argument('--foo', nargs='?')
716+
parser.add_argument('bar')
717+
parser.add_argument('bax')
718+
args = parser.parse_args(['--foo', 'a', 'b'])
719+
self.assertIsNone(args.foo)
720+
self.assertEqual(args.bar, 'a')
721+
self.assertEqual(args.bax, 'b')
722+
723+
@unittest.expectedFailure
724+
def test_does_not_steal_two_required_positional_interspersed(self):
725+
# https://github.com/python/cpython/issues/53584
726+
parser = ErrorRaisingArgumentParser()
727+
parser.add_argument('--foo', nargs='?')
728+
parser.add_argument('--bar', nargs='?')
729+
parser.add_argument('baz')
730+
parser.add_argument('bax')
731+
args = parser.parse_args(['--foo', 'a', '--bar', 'b'])
732+
self.assertEqual(args.baz, 'a')
733+
self.assertEqual(args.bax, 'b')
734+
735+
def test_greedy_when_positional_also_optional(self):
736+
# https://github.com/python/cpython/issues/53584
737+
# When the following positional is also optional, --foo taking the arg
738+
# is acceptable (both are optional, no unambiguous correct answer).
739+
parser = ErrorRaisingArgumentParser()
740+
parser.add_argument('--foo', nargs='?')
741+
parser.add_argument('bar', nargs='?')
742+
args = parser.parse_args(['--foo', 'abc'])
743+
self.assertEqual(args.foo, 'abc')
744+
self.assertIsNone(args.bar)
745+
690746

691747
class TestOptionalsNargsZeroOrMore(ParserTestCase):
692748
"""Tests specifying args for an Optional that accepts zero or more"""
@@ -706,6 +762,16 @@ class TestOptionalsNargsZeroOrMore(ParserTestCase):
706762
('-y a b', NS(x=None, y=['a', 'b'])),
707763
]
708764

765+
@unittest.expectedFailure
766+
def test_does_not_steal_required_positional(self):
767+
# https://github.com/python/cpython/issues/53584
768+
parser = ErrorRaisingArgumentParser()
769+
parser.add_argument('--foo', nargs='*')
770+
parser.add_argument('bar')
771+
args = parser.parse_args(['--foo', 'abc'])
772+
self.assertEqual(args.foo, [])
773+
self.assertEqual(args.bar, 'abc')
774+
709775

710776
class TestOptionalsNargsOneOrMore(ParserTestCase):
711777
"""Tests specifying args for an Optional that accepts one or more"""
@@ -723,6 +789,29 @@ class TestOptionalsNargsOneOrMore(ParserTestCase):
723789
('-y a b', NS(x=None, y=['a', 'b'])),
724790
]
725791

792+
@unittest.expectedFailure
793+
def test_does_not_steal_required_positional(self):
794+
# https://github.com/python/cpython/issues/53584
795+
parser = ErrorRaisingArgumentParser()
796+
parser.add_argument('--foo', nargs='+')
797+
parser.add_argument('bar')
798+
args = parser.parse_args(['--foo', 'a', 'b'])
799+
self.assertEqual(args.foo, ['a'])
800+
self.assertEqual(args.bar, 'b')
801+
802+
@unittest.expectedFailure
803+
def test_does_not_steal_interspersed_required_positional(self):
804+
# https://github.com/python/cpython/issues/53584
805+
parser = ErrorRaisingArgumentParser()
806+
parser.add_argument('--foo', nargs='+')
807+
parser.add_argument('--bar', nargs='+')
808+
parser.add_argument('baz')
809+
parser.add_argument('bax')
810+
args = parser.parse_args(['--foo', 'a', 'c', '--bar', 'b', 'd'])
811+
self.assertEqual(args.foo, ['a'])
812+
self.assertEqual(args.bar, ['b'])
813+
self.assertEqual(args.baz, 'c')
814+
self.assertEqual(args.bax, 'd')
726815

727816
class TestOptionalsChoices(ParserTestCase):
728817
"""Tests specifying the choices for an Optional"""
@@ -6760,6 +6849,25 @@ def test_invalid_args(self):
67606849
parser = ErrorRaisingArgumentParser(prog='PROG')
67616850
self.assertRaises(ArgumentParserError, parser.parse_intermixed_args, ['a'])
67626851

6852+
@unittest.expectedFailure
6853+
def test_variable_nargs_option_before_positional(self):
6854+
# https://github.com/python/cpython/issues/53584
6855+
parser = ErrorRaisingArgumentParser()
6856+
parser.add_argument('--foo', nargs='?')
6857+
parser.add_argument('bar')
6858+
args = parser.parse_intermixed_args(['--foo', 'abc'])
6859+
self.assertIsNone(args.foo)
6860+
self.assertEqual(args.bar, 'abc')
6861+
6862+
def test_variable_nargs_option_after_positional(self):
6863+
# https://github.com/python/cpython/issues/53584
6864+
parser = ErrorRaisingArgumentParser()
6865+
parser.add_argument('--foo', nargs='?')
6866+
parser.add_argument('bar')
6867+
args = parser.parse_intermixed_args(['abc', '--foo', 'xyz'])
6868+
self.assertEqual(args.foo, 'xyz')
6869+
self.assertEqual(args.bar, 'abc')
6870+
67636871

67646872
class TestIntermixedMessageContentError(TestCase):
67656873
# case where Intermixed gives different error message

0 commit comments

Comments
 (0)