将 argparse 别名解析回原始命令 [英] Resolve argparse alias back to the original command
问题描述
我正在使用具有别名的子解析器/子命令.
I'm using a subparser/subcommand that has an alias.
我正在使用子解析器的 dest
选项来存储子命令的名称,以便我以后可以获取它.
I'm using the dest
option for the subparser to store the name of the subcommand so I can get it later.
当前,如果子命令的名称是 reallyLongName
并且别名是 r
(比如说),则 dest
选项存储 reallyLongName
或 r
完全正确 - 我输入的任何内容都会被存储.这很烦人,因为我现在必须检查命令的名称或其任何别名以识别命令.
Currently if the subcommand's name is reallyLongName
and the alias is r
(say) then the dest
option stores either reallyLongName
or r
exactly - whatever I typed in gets stored. This is annoying because I now have to check for the name of the command or any of its aliases in order to identify the command.
有没有办法让 argparse 将子命令的名称存储在 dest
字段中的某种单一的、规范的文本字符串中?
Is there a way to get argparse to store the subcommand's name in the dest
field in some sort of single, canonical text string?
例如,给定以下代码:
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
对两者都有一个规范的值,例如:
Desired result: command
has a single, canonical value for both, for example:
Namespace(command='mySubcommand')
Namespace(command='mySubcommand')
推荐答案
这出奇地难以挖掘.当您添加子解析器时,它会存储在父级 ._actions
属性中.从那里开始,它只是挖掘属性以获得您需要的东西.下面我创建字典以通过 dest 名称引用子解析器参数,然后添加一个函数,让我们将输入的参数重新映射到主参数名称.
This was surprisingly difficult to dig out. When you add a subparser, it gets stored in the parents ._actions
attribute. From there it is just digging through attributes to get what you need. Below I create dictionaries to reference the subparser arguments by the dest name, and then added a function that lets us remap the inputted arguments to the primary argument name.
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
使用您的示例,我们可以使用以下方法重新映射解析参数:
Using your example, we can remap the parse args using:
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 个可能的参数.
Here is an example of it at work with multiple subparser levels.. We have a parser with an optional argument and a subparser. The subparser has 3 possible arguments, the last of which invoke another subparser (a sub-subparser), with 2 possible arguments.
您可以检查顶级解析器或第一级子解析器以查看别名映射.
You can examine either the top level parser or the first level subparser to see alias mappings.
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']}
将其与上述函数结合使用,我们可以将收集到的参数重新映射到它们更长的名称.
Using this with the function above, we can remap the collected args to their longer names.
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')
这篇关于将 argparse 别名解析回原始命令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!