gh-126068: Fix exceptions in the argparse module (GH-126069)

* Only error messages for ArgumentError and ArgumentTypeError are now
  translated.
* ArgumentError is now only used for command line errors, not for logical
  errors in the program.
* TypeError is now raised instead of ValueError for some logical errors.
This commit is contained in:
Serhiy Storchaka 2024-10-30 18:14:27 +02:00 committed by GitHub
parent 1f16df4bfe
commit cc9a183993
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 72 additions and 58 deletions

View file

@ -2055,7 +2055,7 @@ class TestFileTypeMissingInitialization(TestCase):
def test(self):
parser = argparse.ArgumentParser()
with self.assertRaises(ValueError) as cm:
with self.assertRaises(TypeError) as cm:
parser.add_argument('-x', type=argparse.FileType)
self.assertEqual(
@ -2377,11 +2377,24 @@ class TestInvalidAction(TestCase):
self.assertRaises(NotImplementedError, parser.parse_args, ['--foo', 'bar'])
def test_modified_invalid_action(self):
parser = ErrorRaisingArgumentParser()
parser = argparse.ArgumentParser(exit_on_error=False)
action = parser.add_argument('--foo')
# Someone got crazy and did this
action.type = 1
self.assertRaises(ArgumentParserError, parser.parse_args, ['--foo', 'bar'])
self.assertRaisesRegex(TypeError, '1 is not callable',
parser.parse_args, ['--foo', 'bar'])
action.type = ()
self.assertRaisesRegex(TypeError, r'\(\) is not callable',
parser.parse_args, ['--foo', 'bar'])
# It is impossible to distinguish a TypeError raised due to a mismatch
# of the required function arguments from a TypeError raised for an incorrect
# argument value, and using the heavy inspection machinery is not worthwhile
# as it does not reliably work in all cases.
# Therefore, a generic ArgumentError is raised to handle this logical error.
action.type = pow
self.assertRaisesRegex(argparse.ArgumentError,
"argument --foo: invalid pow value: 'bar'",
parser.parse_args, ['--foo', 'bar'])
# ================
@ -2418,7 +2431,7 @@ class TestAddSubparsers(TestCase):
else:
subparsers_kwargs['help'] = 'command help'
subparsers = parser.add_subparsers(**subparsers_kwargs)
self.assertRaisesRegex(argparse.ArgumentError,
self.assertRaisesRegex(ValueError,
'cannot have multiple subparser arguments',
parser.add_subparsers)
@ -5534,20 +5547,27 @@ class TestInvalidArgumentConstructors(TestCase):
self.assertTypeError(action=action)
def test_invalid_option_strings(self):
self.assertValueError('--')
self.assertValueError('---')
self.assertTypeError('-', errmsg='dest= is required')
self.assertTypeError('--', errmsg='dest= is required')
self.assertTypeError('---', errmsg='dest= is required')
def test_invalid_prefix(self):
self.assertValueError('--foo', '+foo')
self.assertValueError('--foo', '+foo',
errmsg='must start with a character')
def test_invalid_type(self):
self.assertValueError('--foo', type='int')
self.assertValueError('--foo', type=(int, float))
self.assertTypeError('--foo', type='int',
errmsg="'int' is not callable")
self.assertTypeError('--foo', type=(int, float),
errmsg='is not callable')
def test_invalid_action(self):
self.assertValueError('-x', action='foo')
self.assertValueError('foo', action='baz')
self.assertValueError('--foo', action=('store', 'append'))
self.assertValueError('-x', action='foo',
errmsg='unknown action')
self.assertValueError('foo', action='baz',
errmsg='unknown action')
self.assertValueError('--foo', action=('store', 'append'),
errmsg='unknown action')
self.assertValueError('--foo', action="store-true",
errmsg='unknown action')
@ -5562,7 +5582,7 @@ class TestInvalidArgumentConstructors(TestCase):
def test_multiple_dest(self):
parser = argparse.ArgumentParser()
parser.add_argument(dest='foo')
with self.assertRaises(ValueError) as cm:
with self.assertRaises(TypeError) as cm:
parser.add_argument('bar', dest='baz')
self.assertIn('dest supplied twice for positional argument,'
' did you mean metavar?',
@ -5733,14 +5753,18 @@ class TestConflictHandling(TestCase):
parser = argparse.ArgumentParser()
sp = parser.add_subparsers()
sp.add_parser('fullname', aliases=['alias'])
self.assertRaises(argparse.ArgumentError,
sp.add_parser, 'fullname')
self.assertRaises(argparse.ArgumentError,
sp.add_parser, 'alias')
self.assertRaises(argparse.ArgumentError,
sp.add_parser, 'other', aliases=['fullname'])
self.assertRaises(argparse.ArgumentError,
sp.add_parser, 'other', aliases=['alias'])
self.assertRaisesRegex(ValueError,
'conflicting subparser: fullname',
sp.add_parser, 'fullname')
self.assertRaisesRegex(ValueError,
'conflicting subparser: alias',
sp.add_parser, 'alias')
self.assertRaisesRegex(ValueError,
'conflicting subparser alias: fullname',
sp.add_parser, 'other', aliases=['fullname'])
self.assertRaisesRegex(ValueError,
'conflicting subparser alias: alias',
sp.add_parser, 'other', aliases=['alias'])
# =============================