4

I am using argparse to propose a mapping option to the users of my script as following:

parser.add_argument('-m', '--mapping_strategy', 
                    help='mapping strategy', 
                    choices=['ZigZag', 
                    'RoundRobin'])

So I can use the script in this way:

> script.py -m ZigZag

I need now to provide a new mapping strategy where the user can specify a custom file describing the mapping. Thus I need now something like:

> script.py -m Custom /home/manu/custom.map

How can I acheive this with argparse ?

1
  • I am just guessing that you have to add another argument to the parser. You could take multiple parameters by adding a parameter nargs='+' to add_argument. For mapping strategy you have already defined your choices. But that would work if you run script.py -m ZigZag RoundRobin Commented Oct 22, 2015 at 8:08

2 Answers 2

3

You could certainly use a simple nargs="+", but this has a few drawbacks, the --help output gets a lot less informative. You have to do your own verification that mapping_strategy[0] is in ['ZigZag', 'RoundRobin', 'Custom'].

Now, another way would be to require a -p parameter for the path to the map, and require that it is set when mapping_strategy == 'Custom'. You could also print out an "ignoring -p argument, only required for Custom mapping strategy" warning message if they supply it with the wrong mapping_strategy.

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('-m', "--mapping_strategy",
                    help='valid strategies: ZigZag, RoundRobin, Custom', 
                    choices=['ZigZag', 
                    'RoundRobin',
                    'Custom']
                    )

parser.add_argument('-p', "--path",
                    help='path to custom map file, required '
                         'if using Custom mapping_strategy', 
                    )

args = parser.parse_args()
if args.mapping_strategy == 'Custom' and args.path == None:
    parser.error('Custom mapping strategy requires a path')
if args.mapping_strategy != 'Custom' and args.path != None:
    print('Ignoring path parameter, only used for Custom mapping_strategy')
print args

You could alternatively use a custom class to validate your parameters. This code gives a much nicer help message, along with better warnings and error checking. It's a little brittle though, as I've duplicated the list of valid_strategies. That's easy to overcome though.

import argparse

class ValidateMapping(argparse.Action):
    def __call__(self, parser, args, values, option_string=None):
        valid_strategies = ('ZigZag', 'RoundRobin', 'Custom')
        strategy = values[0]
        if strategy not in valid_strategies:
            parser.error('Invalid mapping strategy {}'.format(strategy))
        if strategy == 'Custom':
            if len(values) == 1:
                parser.error('Custom mapping strategy requires a path')
            elif len(values) == 2:
                path = values[1]
            elif len(values) > 2:
                path = '"' + ' '.join(values[1:]) + '"'
            setattr(args, self.dest, [strategy, path])
        else:
            if len(values) > 1:
                print "path to map only used by Custom mapping strategy"
                print "ignoring: ",
                for i in range(1, len(values)):
                    print values[i],
                print
            setattr(args, self.dest, strategy)


parser = argparse.ArgumentParser()

parser.add_argument('-m', "--mapping_strategy",
                    help='valid strategies: ZigZag, RoundRobin, Custom', 
                    nargs="+",
                    action=ValidateMapping,
                    metavar=('mapping strategy', 'path to map')
                    )


args = parser.parse_args()
print args

Here's the help output:

$python mapping_strategy.py -h
usage: mapping_strategy.py [-h] [-m mapping strategy [path to map ...]]

optional arguments:
  -h, --help            show this help message and exit
  -m mapping strategy [path to map ...], --mapping_strategy mapping strategy [path to map ...]
                        valid strategies: ZigZag, RoundRobin, Custom

Here's what happens if you supply only a -m:

$ python mapping_strategy.py -m 
usage: mapping_strategy.py [-h] [-m mapping strategy [path to map ...]]
mapping_strategy.py: error: argument -m/--mapping_strategy: expected at least one argument

Here's what you see if you type a -m Custom but don't supply a path:

$ python mapping_strategy.py -m Custom
usage: mapping_strategy.py [-h] [-m mapping strategy [path to map ...]]
mapping_strategy.py: error: Custom mapping strategy requires a path

Here's what happens if you give -m ZigZag and append a meaningless path:

$ python mapping_strategy.py -m ZigZag blah blah
path to map only used by Custom mapping strategy
ignoring:  blah blah

Here's what you get if you supply a Custom choice with a path including spaces:

$ python mapping_strategy.py -m Custom c:\My Documents
Namespace(mapping_strategy=['Custom', '"c:My Documents"'])

But who would use Windows, or have spaces in directory names? Heathens.

Here's what you get if you specify an invalid mapping strategy:

$ python mapping_strategy.py -m Foo
usage: mapping_strategy.py [-h] [-m mapping strategy [path to map ...]]
mapping_strategy.py: error: Invalid mapping strategy Foo
Sign up to request clarification or add additional context in comments.

Comments

2

Change your below line to:

parser.add_argument('-m', '--mapping_strategy', 
                     help='mapping strategy', nargs="+")

This will gather all the positional arguments in a list instead. It will also generate an error if there's not at least one to operate on.

Check out the nargs documentation

3 Comments

Thank you. But in that case the user will be allowed to type anything after the -m option, right ? Or I should do the checking myself ?
@dkol do you have any other solution ?
@ManuelSelva sorry never tried choices with nargs :(

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.