3

I've created an argparse argument, which supports only 3 choices:

parser.add_argument('--param', default='ch1', choices=('ch1', 'ch2', 'ch3'), help='message')

But I have to add a new special choice ch4 which will require an additional value. I.e., my program should support such inputs:

./prog.py --param ch1
./prog.py --param ch2 # or ch3
./prog.py --param ch4='some_additional_variable'

How I can realize this (or some similar behavior)?

nthall
  • 2,847
  • 1
  • 28
  • 36
VeLKerr
  • 2,995
  • 3
  • 24
  • 47
  • 1
    I don't think this comes "out of the box". You'll probably need to drop `choices`, add `nargs=*` and then do the validation yourself (either after parsing the command, or in a custom `type` function or `argparse.Action`). – mgilson Mar 24 '16 at 14:51
  • what usage and help would you want to see? – hpaulj Mar 24 '16 at 16:50

2 Answers2

2

You can workaround it by requiring an additional argument if ch4 param is set:

import argparse

parser = argparse.ArgumentParser(description='...')
parser.add_argument('--param', default='ch1', choices=('ch1', 'ch2', 'ch3', 'ch4'))
parser.add_argument('--setting', help="Required if param is set to ch4")

args = parser.parse_args()    
if args.param == "ch4" and not args.setting:
    parser.error("--setting is required if param is ch4")

Usage:

$ python test.py --param ch1
$ python test.py --param ch2
$ python test.py --param ch3
$ python test.py --param ch4
usage: test.py [-h] [--param {ch1,ch2,ch3,ch4}] [--setting SETTING]
test.py: error: --setting is required if param is ch
$ python test.py --param ch4 --setting "some_value"
$
Community
  • 1
  • 1
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
1

The add_argument method takes type keyword parameter. Any callable can be passed as an argument for this parameter.
Here,s a class which can be passed to the add_argument method and this may satisfy your requirement.

import argparse

class Arg(object):
    def __init__(self, value):
        self.value = value
        if '=' in value:
            self.value = value.split('=', 1)

    def __str__(self):
        return '{}=<{}>'.format(self.value[0], self.value[1]) if isinstance(self.value, list) else self.value

    def __repr__(self):
        if isinstance(self.value, list):
            return repr('{}=<value>'.format(self.value[0]))
        return '{}'.format(repr(self.value))

    def __eq__(self, value):
        return '{}'.format(repr(value)) == repr(self)

parser = argparse.ArgumentParser()
parser.add_argument('--param', default='ch1', choices=('ch1', 'ch2', 'ch3', 'ch4=<value>'), type=Arg)

args = parser.parse_args('--param ch4=123'.split())
print('choice {}, value {}'.format(args.param, repr(args.param.value)))

args = parser.parse_args([])
print('choice {}, value {}'.format(args.param, repr(args.param.value)))

args = parser.parse_args('--param ch3'.split())
print('choice {}, value {}'.format(args.param, repr(args.param.value)))

Output;

choice ch4=<123>, value ['ch4', '123']
choice ch1, value 'ch1'
choice ch3, value 'ch3'

args.param is an instance of Arg. args.param.value is either an str or a list if choice is ch4='some_value'

Nizam Mohamed
  • 8,751
  • 24
  • 32