mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	[3.13] gh-125355: Rewrite parse_intermixed_args() in argparse (GH-125356) (GH-125834)
* The parser no longer changes temporarily during parsing.
* Default values are not processed twice.
* Required mutually exclusive groups containing positional arguments are
  now supported.
* The missing arguments report now includes the names of all required
  optional and positional arguments.
* Unknown options can be intermixed with positional arguments in
  parse_known_intermixed_args().
(cherry picked from commit 759a54d28f)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
			
			
This commit is contained in:
		
							parent
							
								
									e3bfe1e756
								
							
						
					
					
						commit
						1fe63b15eb
					
				
					 3 changed files with 80 additions and 82 deletions
				
			
		|  | @ -6257,12 +6257,23 @@ def test_basic(self): | |||
|         # cannot parse the '1,2,3' | ||||
|         self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[1]), args) | ||||
|         self.assertEqual(["2", "3"], extras) | ||||
|         args, extras = parser.parse_known_intermixed_args(argv) | ||||
|         self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[1, 2, 3]), args) | ||||
|         self.assertEqual([], extras) | ||||
| 
 | ||||
|         # unknown optionals go into extras | ||||
|         argv = 'cmd --foo x --error 1 2 --bar y 3'.split() | ||||
|         args, extras = parser.parse_known_intermixed_args(argv) | ||||
|         self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[1, 2, 3]), args) | ||||
|         self.assertEqual(['--error'], extras) | ||||
|         argv = 'cmd --foo x 1 --error 2 --bar y 3'.split() | ||||
|         args, extras = parser.parse_known_intermixed_args(argv) | ||||
|         # unknown optionals go into extras | ||||
|         self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[1]), args) | ||||
|         self.assertEqual(['--error', '2', '3'], extras) | ||||
|         self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[1, 2, 3]), args) | ||||
|         self.assertEqual(['--error'], extras) | ||||
|         argv = 'cmd --foo x 1 2 --error --bar y 3'.split() | ||||
|         args, extras = parser.parse_known_intermixed_args(argv) | ||||
|         self.assertEqual(NS(bar='y', cmd='cmd', foo='x', rest=[1, 2, 3]), args) | ||||
|         self.assertEqual(['--error'], extras) | ||||
| 
 | ||||
|         # restores attributes that were temporarily changed | ||||
|         self.assertIsNone(parser.usage) | ||||
|  | @ -6281,37 +6292,48 @@ def test_remainder(self): | |||
|             parser.parse_intermixed_args(argv) | ||||
|         self.assertRegex(str(cm.exception), r'\.\.\.') | ||||
| 
 | ||||
|     def test_exclusive(self): | ||||
|         # mutually exclusive group; intermixed works fine | ||||
|         parser = ErrorRaisingArgumentParser(prog='PROG') | ||||
|     def test_required_exclusive(self): | ||||
|         # required mutually exclusive group; intermixed works fine | ||||
|         parser = argparse.ArgumentParser(prog='PROG', exit_on_error=False) | ||||
|         group = parser.add_mutually_exclusive_group(required=True) | ||||
|         group.add_argument('--foo', action='store_true', help='FOO') | ||||
|         group.add_argument('--spam', help='SPAM') | ||||
|         parser.add_argument('badger', nargs='*', default='X', help='BADGER') | ||||
|         args = parser.parse_intermixed_args('--foo 1 2'.split()) | ||||
|         self.assertEqual(NS(badger=['1', '2'], foo=True, spam=None), args) | ||||
|         args = parser.parse_intermixed_args('1 --foo 2'.split()) | ||||
|         self.assertEqual(NS(badger=['1', '2'], foo=True, spam=None), args) | ||||
|         self.assertRaises(ArgumentParserError, parser.parse_intermixed_args, '1 2'.split()) | ||||
|         self.assertRaisesRegex(argparse.ArgumentError, | ||||
|                 'one of the arguments --foo --spam is required', | ||||
|                 parser.parse_intermixed_args, '1 2'.split()) | ||||
|         self.assertEqual(group.required, True) | ||||
| 
 | ||||
|     def test_exclusive_incompatible(self): | ||||
|         # mutually exclusive group including positional - fail | ||||
|         parser = ErrorRaisingArgumentParser(prog='PROG') | ||||
|     def test_required_exclusive_with_positional(self): | ||||
|         # required mutually exclusive group with positional argument | ||||
|         parser = argparse.ArgumentParser(prog='PROG', exit_on_error=False) | ||||
|         group = parser.add_mutually_exclusive_group(required=True) | ||||
|         group.add_argument('--foo', action='store_true', help='FOO') | ||||
|         group.add_argument('--spam', help='SPAM') | ||||
|         group.add_argument('badger', nargs='*', default='X', help='BADGER') | ||||
|         self.assertRaises(TypeError, parser.parse_intermixed_args, []) | ||||
|         args = parser.parse_intermixed_args(['--foo']) | ||||
|         self.assertEqual(NS(foo=True, spam=None, badger='X'), args) | ||||
|         args = parser.parse_intermixed_args(['a', 'b']) | ||||
|         self.assertEqual(NS(foo=False, spam=None, badger=['a', 'b']), args) | ||||
|         self.assertRaisesRegex(argparse.ArgumentError, | ||||
|                 'one of the arguments --foo --spam badger is required', | ||||
|                 parser.parse_intermixed_args, []) | ||||
|         self.assertRaisesRegex(argparse.ArgumentError, | ||||
|                 'argument badger: not allowed with argument --foo', | ||||
|                 parser.parse_intermixed_args, ['--foo', 'a', 'b']) | ||||
|         self.assertRaisesRegex(argparse.ArgumentError, | ||||
|                 'argument badger: not allowed with argument --foo', | ||||
|                 parser.parse_intermixed_args, ['a', '--foo', 'b']) | ||||
|         self.assertEqual(group.required, True) | ||||
| 
 | ||||
|     def test_invalid_args(self): | ||||
|         parser = ErrorRaisingArgumentParser(prog='PROG') | ||||
|         self.assertRaises(ArgumentParserError, parser.parse_intermixed_args, ['a']) | ||||
| 
 | ||||
|         parser = ErrorRaisingArgumentParser(prog='PROG') | ||||
|         parser.add_argument('--foo', nargs="*") | ||||
|         parser.add_argument('foo') | ||||
|         with self.assertWarns(UserWarning): | ||||
|             parser.parse_intermixed_args(['hello', '--foo']) | ||||
| 
 | ||||
| class TestIntermixedMessageContentError(TestCase): | ||||
|     # case where Intermixed gives different error message | ||||
|  | @ -6330,7 +6352,7 @@ def test_missing_argument_name_in_message(self): | |||
|         with self.assertRaises(ArgumentParserError) as cm: | ||||
|             parser.parse_intermixed_args([]) | ||||
|         msg = str(cm.exception) | ||||
|         self.assertNotRegex(msg, 'req_pos') | ||||
|         self.assertRegex(msg, 'req_pos') | ||||
|         self.assertRegex(msg, 'req_opt') | ||||
| 
 | ||||
| # ========================== | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Miss Islington (bot)
						Miss Islington (bot)