32

I have the following code which attempts to get the DUT VID from the invoked command line:

parser = argparse.ArgumentParser(description='A Test',
                                 formatter_class=argparse.ArgumentDefaultsHelpFormatter
                                 )
group.add_argument("--vid",
                   type=int,
                   help="vid of DUT")
options = parser.parse_args()

Consider the command line "python test.py --vid 0xabcd" I notice that argparse is raising an exception on this as it fails to complete the call int('0xabcd') because it is base 16. How do I get argparse to correctly handle this?

devanl
  • 1,232
  • 1
  • 10
  • 20
  • Give it another `type` function that does work. It may have to be one that you write yourself. – hpaulj Aug 26 '14 at 22:05

2 Answers2

49

argparse is looking to create a callable type conversion from the 'type' value:

    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))
            msg = _('invalid %s value: %r')
            raise ArgumentError(action, msg % (name, arg_string))

        # return the converted value
        return result

By default int() is set to base 10. In order to accommodate base 16 and base 10 parameters we can enable auto base detection:

    def auto_int(x):
        return int(x, 0)
    ...
    group.add_argument('--vid',
                       type=auto_int,
                       help='vid of DUT')

Note the type is update to be 'auto_int'.

Dev Khadka
  • 5,142
  • 4
  • 19
  • 33
devanl
  • 1,232
  • 1
  • 10
  • 20
  • 2
    Just as a variation on this, `type=functools.partial(int, base=0)` would work without an explicitly Python level function definition (no `def`, no `lambda`). – ShadowRanger Oct 11 '20 at 13:31
29

Not enough reputation to comment on the other answer.

If you don't want to define the auto_int function, I found that this works very cleanly with a lambda.

group.add_argument('--vid',
                   type=lambda x: int(x,0),
                   help='vid of DUT')
Joshua Ryan
  • 628
  • 5
  • 12
  • 2
    @devanl [Python has supported lambdas since 1.0](https://en.wikipedia.org/wiki/History_of_Python#Version_1) . – Joshua Ryan Jun 07 '18 at 15:28
  • 5
    When you use a lambda, the error message printed by argparse will say `invalid value` instead of `invalid auto_int value`. – mic_e Aug 14 '19 at 15:11
  • 3
    You can do `functools.wraps(int)(lambda x: int(x,0))` or `functools.wraps(int)(functools.partial(int, base=0))` and then error messages will refer to `invalid int value`. – Kevin Aug 19 '21 at 04:33