1

I'm using Python version 3.10.1. I'm trying to use the argparse library to handle arguments, including handling invalid arguments gracefully. However, I'm encountering a mind-boggling traceback. Here's the problem code:

import argparse

def check_positive(value):
    try:
        value = int(value)
        if value <= 0:
            raise argparse.ArgumentError(f"{value} is not a positive integer.")
    except ValueError:
        raise argparse.ArgumentError("Score threshold must be a positive integer.")
    return value


if __name__ == "__main__":
    parser = argparse.ArgumentParser(exit_on_error=False)
    parser.add_argument("file", help="a help message")
    parser.add_argument("score_threshold", type=check_positive, help="another help message")
    args = parser.parse_args()

As a test, I specify the argument -102 as the score threshold. This should enter the if statement, and then raise the argparse.ArgumentError. However, in doing so, I encounter another exception:

Traceback (most recent call last):
  File "C:\Program Files\Python310\lib\argparse.py", line 2479, in _get_value
    result = type_func(arg_string)
  File "<snip>", line 151, in check_positive
    raise argparse.ArgumentError(f"{value} is not a positive integer.")
TypeError: ArgumentError.__init__() missing 1 required positional argument: 'message'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<snip>", line 166, in <module>
    args = parser.parse_args()
  File "C:\Program Files\Python310\lib\argparse.py", line 1821, in parse_args
    args, argv = self.parse_known_args(args, namespace)
  File "C:\Program Files\Python310\lib\argparse.py", line 1859, in parse_known_args
    namespace, args = self._parse_known_args(args, namespace)
  File "C:\Program Files\Python310\lib\argparse.py", line 2066, in _parse_known_args
    stop_index = consume_positionals(start_index)
  File "C:\Program Files\Python310\lib\argparse.py", line 2022, in consume_positionals
    take_action(action, args)
  File "C:\Program Files\Python310\lib\argparse.py", line 1915, in take_action
    argument_values = self._get_values(action, argument_strings)
  File "C:\Program Files\Python310\lib\argparse.py", line 2446, in _get_values
    value = self._get_value(action, arg_string)
  File "C:\Program Files\Python310\lib\argparse.py", line 2492, in _get_value
    raise ArgumentError(action, msg % args)
argparse.ArgumentError: argument score_threshold: invalid check_positive value: '-102'

I have absolutely no idea why Python is complaining. I'm utterly baffled. Aren't I handling the error as expected? It should simply raise the argparse.ArgumentError and print -102 is not a positive integer. Also, I have no idea what the hell TypeError: ArgumentError.__init__() missing 1 required positional argument: 'message' means. I should only need to provide an error message, right? Is there some change to argparse.ArgumentError that I'm not aware of? Searching the Internet has yielded zero fruit, so I'm really scratching my head on this.

1 Answer 1

2

The init for ArgumentError is

def __init__(self, argument, message):
   ...

That's why you get TypeError: ArgumentError.__init__() missing 1 required positional argument: 'message' when you only provide one argument.

Look in the argparse.py code to see examples of where ArgumentError is raised. In `_get_value

The developer's intentions that an error in type function should raise a ArgumentTypeError. In _get_value this class of error is caught, and converted to an ArgumentError.

def _get_value(self, action, arg_string):
    type_func = self._registry_get('type', action.type, action.type)
    if not callable(type_func):
        msg = _('%r is not callable')
        raise ArgumentError(action, msg % type_func)

    # convert the value to the appropriate type
    try:
        result = type_func(arg_string)

    # ArgumentTypeErrors indicate errors
    except ArgumentTypeError:
        name = getattr(action.type, '__name__', repr(action.type))
        msg = str(_sys.exc_info()[1])
        raise ArgumentError(action, msg)

    # TypeErrors or ValueErrors also indicate errors
    except (TypeError, ValueError):
        name = getattr(action.type, '__name__', repr(action.type))
        args = {'type': name, 'value': arg_string}
        msg = _('invalid %(type)s value: %(value)r')
        raise ArgumentError(action, msg % args)

TypeError and ValueError which are produced by functions like int and float, are converted to a standardized error message. The ArgumentTypeError message is passed through unchanged.

The long traceback just shows how far down the parse_args calling stack this error occurs.

These two exception classes are simple, and relatively clear in the argparse.py code, but not documented.

Under type the documentation says:

The argument to type can be any callable that accepts a single 
string. If the function raises ArgumentTypeError, TypeError, or 
ValueError, the exception is caught and a nicely formatted error 
message is displayed. No other exception types are handled.

And with the addition of the exit_on_error=False parameter, there are few code examples with ArgumentError.

Since few people have tried to do what you've done, you won't find any help on a "internet search". That's only useful for common, but "unique" errors. For this kind of error, studying the argparse.py code is the only option.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.