简体   繁体   中英

Python argparse AssertionError from metavar `[[USER@]HOST:]FILE`

Argparse 1.1 or 1.4 fails with AssertionError - the weird regex that reads the metavar value seems to blow up argparse - Possibly related to Python argparse AssertionError when number of arguments exceeds threshold ?

Is there any alternative way to create or use the metavar [[USER@]HOST:]FILE ?

Test setup:

$ virtualenv-3.5 --always-copy test2
$ ./test2/bin/pip install argparse

Python code test.py:

#!/Users/[username]/Development/test2/bin/python3.5

import os
import sys
print('sys.prefix', sys.prefix)
sys.path.insert(
    0, os.path.join(sys.prefix, 'lib/python3.5/site-packages'))

import argparse
print('argparse', argparse.__version__)

parser = argparse.ArgumentParser()
parser.add_argument(
    'files',
    metavar='[[USER@]HOST:]FILE',
    nargs=argparse.PARSER,
    )
parser.add_argument('-a', '-A', metavar='PTRN', dest='patterns', default=[])
parser.add_argument('-b', '-B', metavar='PTRN', dest='patterns', default=[])
parser.add_argument('-c', '-C', metavar='PTRN', dest='patterns', default=[])
parser.add_argument('-d', '-D', metavar='PTRN', dest='patterns', default=[])
parser.add_argument('-e', '-E', metavar='PTRN', dest='patterns', default=[])
parser.add_argument('-f', '-F', metavar='PTRN', dest='patterns', default=[])
print(parser.parse_args())

Shell output:

$ ./test.py
sys.prefix /Users/[username]/Development/test2/bin/..
argparse 1.4.0
Traceback (most recent call last):
  File "./test.py", line 24, in <module>
    print(parser.parse_args())
  File "/Users/[username]/Development/test2/bin/../lib/python3.5/site-packages/argparse.py", line 1725, in parse_args
    args, argv = self.parse_known_args(args, namespace)
  File "/Users/[username]/Development/test2/bin/../lib/python3.5/site-packages/argparse.py", line 1754, in parse_known_args
    namespace, args = self._parse_known_args(args, namespace)
  File "/Users/[username]/Development/test2/bin/../lib/python3.5/site-packages/argparse.py", line 1971, in _parse_known_args
    self.error(_('too few arguments'))
  File "/Users/[username]/Development/test2/bin/../lib/python3.5/site-packages/argparse.py", line 2391, in error
    self.print_usage(_sys.stderr)
  File "/Users/[username]/Development/test2/bin/../lib/python3.5/site-packages/argparse.py", line 2353, in print_usage
    self._print_message(self.format_usage(), file)
  File "/Users/[username]/Development/test2/bin/../lib/python3.5/site-packages/argparse.py", line 2309, in format_usage
    return formatter.format_help()
  File "/Users/[username]/Development/test2/bin/../lib/python3.5/site-packages/argparse.py", line 306, in format_help
    help = self._root_section.format_help()
  File "/Users/[username]/Development/test2/bin/../lib/python3.5/site-packages/argparse.py", line 236, in format_help
    func(*args)
  File "/Users/[username]/Development/test2/bin/../lib/python3.5/site-packages/argparse.py", line 358, in _format_usage
    assert ' '.join(pos_parts) == pos_usage
AssertionError

The workaround is to create a custom help formatter and do post-processing using a sanitized value. One way to do so can be like so:

class MyHelpFormatter(argparse.HelpFormatter):

    def _format_usage(self, usage, actions, groups, prefix):
        result = super(MyHelpFormatter, self)._format_usage(
            usage, actions, groups, prefix)
        return result.format(user_host_file='[[USER@]HOST]:FILE')

Then construct the parser using the custom formattter

parser = argparse.ArgumentParser(formatter_class=MyHelpFormatter)
parser.add_argument(
    'files',
    metavar='{user_host_file}',
    nargs=argparse.PARSER,
    )

Plugging the rest into the code supplied, something like this is produced

$ python demo.py 
('argparse', '1.1')
usage: demo.py [-h] [-a PTRN] [-b PTRN] [-c PTRN] [-d PTRN] [-e PTRN]
               [-f PTRN]
               [[USER@]HOST]:FILE ...
demo.py: error: too few arguments

Naturally, the exact strategy can be as simple as using the str.replace method in the formatter, or more complicated than the str.format method.

Yes, there's a known bug in the argparse usage formatter. Characters in the metavar like brackets produce this assertion error. I could point out the bug/issue or explain the problem. But the simplest solution is to just change your METAVAR to something simpler. Put the extra information in the help line. Another simple option is to provide a custom usage parameter.

http://bugs.python.org/issue11874

Subclassing the HelpFormatter and replacing one or two methods is also good argparse practice. But that requires digging into the code, and understanding what needs to be replaced. The other answer is a good start. The changes suggest in the issue patch are more complicated because it is trying to be general purpose (including the handling of mutually exclusive groups and usage that can span several lines).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM