【问题标题】:Python argparse AssertionError from metavar `[[USER@]HOST:]FILE`来自元变量的 Python argparse AssertionError `[[USER@]HOST:]FILE`
【发布时间】:2017-04-21 20:29:40
【问题描述】:

Argparse 1.1 或 1.4 因 AssertionError 而失败 - 读取元变量值的奇怪正则表达式似乎炸毁了 argparse - 可能与 Python argparse AssertionError when number of arguments exceeds threshold 相关?

有没有其他方法可以创建或使用元变量[[USER@]HOST:]FILE

测试设置:

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

Python 代码 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())

外壳输出:

$ ./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

【问题讨论】:

    标签: python argparse


    【解决方案1】:

    解决方法是创建自定义帮助格式化程序并使用经过清理的值进行后处理。一种方法可以是这样:

    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')
    

    然后使用自定义格式化程序构造解析器

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

    将其余部分插入提供的代码中,生成类似这样的代码

    $ 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
    

    当然,确切的策略可以像在格式化程序中使用str.replace 方法一样简单,也可以比str.format 方法更复杂。

    【讨论】:

    • 不错的 hack - 您还需要覆盖方法 _format_action_invocation 以完成 hack。
    • 酷。是的,这很痛苦,我最近的项目有一个大约 200 行长的模块,可以解决/增强所有支持的 Python 版本中该模块的缺陷; 2.7 和 3.3(可能还有 3.5)之间的错误修复可能会导致各种不同/意外的行为。
    • 发现了其他东西 - 还需要覆盖方法 ArgumentParser.error
    【解决方案2】:

    是的,argparse usage 格式化程序中有一个已知错误。元变量(如括号)中的字符会产生此断言错误。我可以指出错误/问题或解释问题。但最简单的解决方案是将您的METAVAR 更改为更简单的东西。将额外信息放在帮助行中。另一个简单的选项是提供自定义usage 参数。

    http://bugs.python.org/issue11874

    继承HelpFormatter 并替换一两个方法也是很好的argparse 做法。但这需要深入研究代码,并了解需要替换的内容。另一个答案是一个好的开始。问题补丁中建议的更改更加复杂,因为它试图通用(包括处理互斥组和可以跨多行的用法)。

    【讨论】:

    • 啊,你发现了错误问题,谢谢 - 我的 google-foo 让我失望了!我知道问题的根源是 _format_usage 中的正则表达式 - 诀窍是如何在不重写 argparse 的几百行代码的情况下修复该类
    • 断言用于确保正确拆分使用字符串。但是拆分仅用于多行情况。如果使用适合一行,则不需要此拆分。我的大补丁创建了一个使用字符串列表,最后只将它们连接成一个。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-19
    • 2014-07-26
    • 2013-01-02
    • 1970-01-01
    • 2015-11-25
    • 2018-06-24
    • 2021-03-06
    相关资源
    最近更新 更多