Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python argparse multiple metavar names

I am working with the argparse library in python. At some time, I use an argument called param that takes 2 args: a key and a value. The line of code I use is the following:

parser.add_argument("-p", "--param", nargs=2, action="append", 
                    help="key and value for query", 
                    type=str, metavar="key value"
                    )

What's wrong here is when I call the help, it displays like this:

optional arguments:
    -h, --help            show this help message and exit
    -p key value key value, --param key value key value
                          key and value for query parameters

The name 'key value' is repeated twice. I tried with lists, and generators but the only way i found was creating a little class containing the different values and yielding them when ask to __str__ like this:

class Meta:
    def __init__(self, iterable):
        self.gene = itertools.cycle(iterable)

    def __str__(self):
        return self.gene.__next__()

and I call add_argument like this:

parser.add_argument("-p", "--param", nargs=2, action="append", 
                    help="key and value for query parameters",
                    type=str, metavar=Meta(["key", "value"])
                    )

And it displays correctly:

-p key value, --param key value
                    key and value for query parameters

But I find pretty ugly using a temporary class like Meta, and I feel like there must be another (better) way of doing this. Am I doing it right ?

like image 678
pLOPeGG Avatar asked Jun 21 '18 09:06

pLOPeGG


People also ask

How do you pass multiple arguments in Python Argparse?

Multiple Input ArgumentsUsing the nargs parameter in add_argument() , you can specify the number (or arbitrary number) of inputs the argument should expect. In this example named sum.py , the --value argument takes in 3 integers and will print the sum.

What does Metavar do in Argparse?

Python argparse metavar The metavar option gives a name to the expected value in error and help outputs. The example names the expected value value . The default name is V . The given name is shown in the help output.

What does Store_true mean in Argparse?

2. I would add this: store_true means if true was specified then set param value but otherwise leave it to None. If default was also specified then param is set to that value instead of leaving it to None.

How do you pass arguments to Argparse?

First, we need the argparse package, so we go ahead and import it on Line 2. On Line 5 we instantiate the ArgumentParser object as ap . Then on Lines 6 and 7 we add our only argument, --name . We must specify both shorthand ( -n ) and longhand versions ( --name ) where either flag could be used in the command line.


2 Answers

From scrolling the doc deeply, I've found my answer

Different values of nargs may cause the metavar to be used multiple times. Providing a tuple to metavar specifies a different display for each of the arguments:

indeed, this works perfectly fine:

parser.add_argument("-p", "--param", nargs=2, action="append", 
                    help="key and value for query parameters",
                    type=str, metavar=("key", "value")
                    )
like image 178
pLOPeGG Avatar answered Sep 28 '22 09:09

pLOPeGG


The metavar handler isn't particularly sophisticated, but does take advantage of the information that tuple class provides.

It wasn't obvious as to why your class worked, so I dug into the code.

Metavar is handled in the Formatter class with:

def _metavar_formatter(self, action, default_metavar):
    if action.metavar is not None:
        result = action.metavar
    elif action.choices is not None:
        choice_strs = [str(choice) for choice in action.choices]
        result = '{%s}' % ','.join(choice_strs)
    else:
        result = default_metavar

    def format(tuple_size):
        if isinstance(result, tuple):
            return result
        else:
            return (result, ) * tuple_size
    return format

and

def _format_args(self, action, default_metavar):
    get_metavar = self._metavar_formatter(action, default_metavar)
    ....
    else:
        # for numeric nargs
        formats = ['%s' for _ in range(action.nargs)]
        result = ' '.join(formats) % get_metavar(action.nargs)
    return result

So with your Meta:

In [261]: x = Meta(['one', 'two'])
In [262]: x
Out[262]: <__main__.Meta at 0x7f36980f65c0>
In [263]: x = (x,)*2
In [264]: x
Out[264]: (<__main__.Meta at 0x7f36980f65c0>, <__main__.Meta at 0x7f36980f65c0>)
In [265]: '%s %s'%x
Out[265]: 'one two'

With the tuple metavar:

In [266]: '%s %s'%('one','two')
Out[266]: 'one two'

and with a single string

In [267]: '%s %s'%(('one two',)*2)
Out[267]: 'one two one two'
like image 23
hpaulj Avatar answered Sep 28 '22 08:09

hpaulj