Reputation: 1637
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 ?
Upvotes: 13
Views: 6898
Reputation: 1637
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")
)
Upvotes: 26
Reputation: 231665
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'
Upvotes: 1