【问题标题】:Resolve argparse alias back to the original command将 argparse 别名解析回原始命令
【发布时间】:2020-04-17 00:12:34
【问题描述】:

我正在使用subparser/subcommand that has an alias

我正在使用子解析器的dest 选项来存储子命令的名称,以便稍后获取。

当前,如果子命令的名称是 reallyLongName 并且别名是 r(比如说),那么 dest 选项存储 reallyLongName r 准确 - 无论我输入什么被存储。这很烦人,因为我现在必须检查命令的名称或其任何别名才能识别命令。

有没有办法让 argparse 以某种单一的规范文本字符串将子命令的名称存储在 dest 字段中?

例如,给定以下代码:

import argparse

parser = argparse.ArgumentParser()

subparsers = parser.add_subparsers(dest='command', help='sub-command help')

parser_ag = subparsers.add_parser(  'mySubcommand',
                                    aliases=['m'],
                                    help='Subcommand help')

print(parser.parse_args('mySubcommand'.split()))

print(parser.parse_args('m'.split()))

产生以下输出:

Namespace(command='mySubcommand')
Namespace(command='m')

期望的结果:command 对两者都有一个单一的规范值,例如:

Namespace(command='mySubcommand')
Namespace(command='mySubcommand')

【问题讨论】:

    标签: python argparse


    【解决方案1】:

    有一个 Python 错误/问题要求这样做 - 保存“基本”名称,而不是别名。如果不更改 argparse.py 代码,您将无法更改它。我认为更改将仅限于处理子解析器的Action 子类。 https://bugs.python.org/issue36664

    但我指出有更简单的方法来处理这个问题。只需使用set_defaults,如https://docs.python.org/3/library/argparse.html#sub-commands 部分末尾附近所述。有

    parser_foo.set_defaults(func=foo)
    

    用于设置子解析器特定的函数,但它也可以用于设置“基本”名称。

    parser_foo.set_defaults(name='theIncrediblyLongAlias')
    

    【讨论】:

      【解决方案2】:

      这出人意料地难以挖掘。添加子解析器时,它会存储在父级 ._actions 属性中。从那里它只是挖掘属性来获得你需要的东西。下面我创建字典以通过 dest 名称引用子解析器参数,然后添加一个函数,让我们将输入的参数重新映射到主参数名称。

      from collections import defaultdict
      
      def get_subparser_aliases(parser, dest):
          out = defaultdict(list)
          prog_str = parser.prog
          dest_dict = {a.dest: a for a in parser._actions}
          try:
              choices = dest_dict.get(dest).choices
          except AttributeError:
              raise AttributeError(f'The parser "{parser}" has no subparser with a `dest` of "{dest}"')
      
          for k, v in choices.items():
              clean_v = v.prog.replace(prog_str, '', 1).strip()
              out[clean_v].append(k)
          return dict(out)
      
      def remap_args(args, mapping, dest):
          setattr(args, dest, mapping.get(getattr(args, dest)))
          return args
      

      使用您的示例,我们可以使用以下方法重新映射解析参数:

      import argparse
      
      parser = argparse.ArgumentParser()
      subparsers = parser.add_subparsers(dest='command', help='sub-command help')
      parser_ag = subparsers.add_parser('mySubcommand',
                                        aliases=['m'],
                                        help='Subcommand help')
      
      args = parser.parse_args('m'.split())
      mapping = get_subparser_aliases(parser, 'command')
      remap_args(args, mapping, 'command')
      print(args)
      # prints:
      Namespace(command='mySubcommand')
      

      这是一个使用多个子解析器级别的示例。我们有一个带有可选参数的解析器和一个子解析器。子解析器有 3 个可能的参数,其中最后一个调用另一个子解析器(子子解析器),有 2 个可能的参数。

      您可以检查顶级解析器或第一级子解析器以查看别名映射。

      import argparse
      
      parser = argparse.ArgumentParser()
      parser.add_argument('--someoption', '-s', action='store_true')
      
      subparser1 = parser.add_subparsers(help='sub-command help', dest='sub1')
      parser_r = subparser1.add_parser('reallyLongName', aliases=['r'])
      parser_r.add_argument('foo', type=int, help='foo help')
      parser_s = subparser1.add_parser('otherReallyLong', aliases=['L'])
      parser_s.add_argument('bar', choices='abc', help='bar help')
      parser_z = subparser1.add_parser('otherOptions', aliases=['oo'])
      
      subparser2 = parser_z.add_subparsers(help='sub-sub-command help', dest='sub2')
      parser_x = subparser2.add_parser('xxx', aliases=['x'])
      parser_x.add_argument('fizz', type=float, help='fizz help')
      parser_y = subparser2.add_parser('yyy', aliases=['y'])
      parser_y.add_argument('blip', help='blip help')
      
      get_subparser_aliases(parser, 'sub1')
      # returns:
      {'reallyLongName': ['reallyLongName', 'r'],
       'otherReallyLong': ['otherReallyLong', 'L'],
       'otherOptions': ['otherOptions', 'oo']}
      
      get_subparser_aliases(parser_z, 'sub2')
      # returns:
      {'xxx': ['xxx', 'x'], 'yyy': ['yyy', 'y']}
      

      将它与上面的函数一起使用,我们可以将收集到的 args 重新映射到更长的名称。

      args = parser.parse_args('-s oo x 1.23'.split())
      print(args)
      # prints:
      Namespace(fizz=1.23, someoption=True, sub1='oo', sub2='x')
      
      for p, dest in zip((parser, parser_z), ('sub1', 'sub2')):
          mapping = get_subparser_aliases(p, dest)
          remap_args(args, mapping, dest)
      
      print(args)
      # prints:
      Namespace(fizz=1.23, someoption=True, sub1='otherOptions', sub2='xxx')
      

      【讨论】:

      • choices = subparsers.choices 会稍微简化搜索。 subparsersargparse._SubParsersAction 类对象。您正在从子解析器help 中使用的prog 中提取完整名称或基本名称。否则,argparse 不会将子解析器名称视为特殊名称。它只是别名之一。
      • 'subparsers._name_parser_map' 和subparsers.choices是同一张地图。
      猜你喜欢
      • 2017-01-31
      • 2013-09-13
      • 2012-12-13
      • 2011-08-20
      • 2020-09-06
      • 1970-01-01
      • 2016-02-19
      • 2017-04-13
      相关资源
      最近更新 更多