Skip to content Skip to sidebar Skip to footer

Python3 Argparse Metavar Brackets Parsed Weirdly

I am using argparse in python3, and I get some strange things: A short version of code that I'm using is: argparser = argparse.ArgumentParser(description='add/remove items') argpar

Solution 1:

It's the () in your metavars that is causing the mangled usage. The usage formatter uses () to mark require mutually exclusive groups, and then removes surplus ones. So it tries to keep ( -o | -t), but change (-o) to -o. Unfortunately that code does not distinguish between the ones it added and the ones you added via the metavar (or help line).

Your line was formatted as:

usage: test.py [-h] [-a Item(s)] [-r Item(s)]

but it removed the outer pair of () which I have replaced with *:

usage: test.py [-h] [-a Item*s)] [-r Item(s*]

http://bugs.python.org/issue11874 focuses on a different usage problem, one which occurs when the usage line is long and needs to be split. But the last 2 posts in that issue deal with this issue.

If you don't like the limitations of the automatic usage formatting, you can give the parser your own custom usage parameter.


Solution 2:

Since you want to have the possibility of having multiple items. Another way to do this with argparse is as follows:

import argparse
argparser = argparse.ArgumentParser(description='add/remove items')
argparser.add_argument('-a', metavar="item", nargs="*", help='add one or more items to the list')
argparser.add_argument('-r', metavar="item", nargs="*", help='remove one or more items from the list')
args = argparser.parse_args()

The key point is the use of nargs="*" (0 or more arguments). The help becomes:

usage: test.py [-h] [-a [item [item ...]]] [-r [item [item ...]]]

This way, you do not have to use "Item(s)", and you also follow a standard practice.

PS: I see what you wanted to do. With action="append", you are actually allowing the user to specify multiple -a and -r options. In this case, you should definitely write "Item" (and not "Item(s)"), since each option takes a single item. This solves your problem too (your help message should indicate that multiple -a and -r options can be given).


Solution 3:

My solution in Python 2.7 was to override argparse.format_usage() and argparse.format_help(). You encode your metavars and then decode them after argparse does its formatting:

FIXES = (('\[', '%lb'), ('\]', '%rb'), ('\(', '%lp'), ('\)', '%rp'))
def encode_parens(text):
    for orig, encoded in FIXES:
        text = re.sub(orig, encoded, text)
    return text
def decode_parens(text):
    for orig, encoded in FIXES:
        text = re.sub(encoded, orig[1:], text)
    return text

class MyArgParser(argparse.ArgumentParser):
    def __init__(self, *args, **kwargs):
        super(MyArgParser, self).__init__(*args, **kwargs)
    def format_usage(self, *args, **kwargs):
        u = super(MyArgParser, self).format_usage(*args, **kwargs)
        return decode_parens(u)
    def format_help(self, *args, **kwargs):
        h = super(MyArgParser, self).format_help(*args, **kwargs)
        return decode_parens(h)

if __name__ == '__main__':
    argparser = MyArgParser(description='add/remove items')
    argparser.add_argument('-a', action='append', metavar=encode_parens("Item(s)"), help='add one or more items to the list')
    argparser.add_argument('-r', action='append', metavar=encode_parens("Item(s)"), help='remove one or more items from the list')
    args = argparser.parse_args()

That yields what you want:

usage: arg.py [-h] [-a Item(s)] [-r Item(s)]

It also fixes square brackets, which argparse doesn't like either.


Post a Comment for "Python3 Argparse Metavar Brackets Parsed Weirdly"