如何使用带有 Python 2.7 的 Argparse 模块设置默认子解析器
How to Set a Default Subparser using Argparse Module with Python 2.7
我正在使用 Python 2.7,我正在尝试使用 argparse 完成类似 shell 的行为。
我的问题,总的来说,我似乎无法在 Python 2.7 中找到一种方法来使用 argparse 的子解析器作为可选。
很难解释我的问题,所以我将描述我的程序需要什么。
程序有两种工作模式:
- 用给定的命令启动程序(每个命令都有自己的
附加参数)和附加参数将 运行 特定
任务。
- 在没有命令的情况下启动程序将启动一个类似于 shell 的程序,该程序可以接受一行参数并像处理
程序以给定行作为参数调用。
所以,例如,如果我的程序支持 'cmd1' 和 'cmd2' 命令,我可以像这样使用它:
python program.py cmd1 additional_args1
python program.py cmd2 additional_args2
或使用 shell 模式:
python program.py
cmd1 additional_args1
cmd2 additional_args2
quit
此外,我还希望我的程序能够采用将影响所有命令的可选全局参数。
为此我使用了 argparse(这是一个纯粹的例子):
parser = argparse.ArgumentParser(description="{} - Version {}".format(PROGRAM_NAME, PROGRAM_VERSION))
parser.add_argument("-i", "--info", help="Display more information")
subparsers = parser.add_subparsers()
parserCmd1 = subparsers.add_parser("cmd1", help="First Command")
parserCmd1.set_defaults(func=cmd1)
parserCmd2 = subparsers.add_parser("cmd2", help="Second Command")
parserCmd2.add_argument("-o", "--output", help="Redirect Output")
parserCmd2.set_defaults(func=cmd2)
所以我可以调用 cmd1(没有额外的参数)或 cmd2(有或没有 -o 标志)。对于这两者,我都可以添加标志 -i 以显示被调用命令的更多信息。
我的问题是我无法激活 shell 模式,因为我必须提供 cmd1 或 cmd2 作为参数(因为使用了强制性的子解析器)
限制:
- 我不能使用Python 3(我知道在那里可以很容易地完成)
- 由于全局可选参数,我无法检查是否没有参数来跳过 arg 解析。
- 我不想添加一个新的命令来调用shell,它必须是在根本没有提供任何命令的情况下
那么如何使用 argparse 和 python 2.7 实现这种行为?
关于 'optional' 子解析器主题的 bug/issue(带链接)。
https://bugs.python.org/issue29298
请注意,这有一个最近的拉取请求。
加上你的脚本和
args = parser.parse_args()
print(args)
结果是
1008:~/mypy$ python3 stack46667843.py
Namespace(info=None)
1009:~/mypy$ python2 stack46667843.py
usage: stack46667843.py [-h] [-i INFO] {cmd1,cmd2} ...
stack46667843.py: error: too few arguments
1009:~/mypy$ python2 stack46667843.py cmd1
Namespace(func=<function cmd1 at 0xb748825c>, info=None)
1011:~/mypy$ python3 stack46667843.py cmd1
Namespace(func=<function cmd1 at 0xb7134dac>, info=None)
我认为 'optional' 子解析器影响了 Py2 和 3 版本,但显然它没有。我将不得不查看代码来验证原因。
在这两种语言中,subparsers.required
是 False
。如果我将它设置为 true
subparsers.required=True
(并在subparsers定义中添加一个dest
),PY3错误信息为
1031:~/mypy$ python3 stack46667843.py
usage: stack46667843.py [-h] [-i INFO] {cmd1,cmd2} ...
stack46667843.py: error: the following arguments are required: cmd
所以这两个版本测试 required
参数的方式不同。 Py3注意required
属性; Py2(显然)使用较早的方法检查 positionals
列表是否为空。
在 parser._parse_known_args
.
快结束时检查所需的参数
Python2.7 包括
# if we didn't use all the Positional objects, there were too few
# arg strings supplied.
if positionals:
self.error(_('too few arguments'))
在检查 action.required
的迭代之前。这就是捕捉丢失的 cmd
并说 too few arguments
.
的原因
因此,需要编辑您的 argparse.py
并删除该块,使其与 Py3 版本的相应部分相匹配。
另一个想法是使用 2 阶段解析。一个处理 'globals',返回它无法处理的字符串。然后有条件地使用子解析器处理额外内容。
import argparse
def cmd1(args):
print('cmd1', args)
def cmd2(args):
print('cmd2', args)
parser1 = argparse.ArgumentParser()
parser1.add_argument("-i", "--info", help="Display more information")
parser2 = argparse.ArgumentParser()
subparsers = parser2.add_subparsers(dest='cmd')
parserCmd1 = subparsers.add_parser("cmd1", help="First Command")
parserCmd1.set_defaults(func=cmd1)
parserCmd2 = subparsers.add_parser("cmd2", help="Second Command")
parserCmd2.add_argument("-o", "--output", help="Redirect Output")
parserCmd2.set_defaults(func=cmd2)
args, extras = parser1.parse_known_args()
if len(extras)>0 and extras[0] in ['cmd1','cmd2']:
args = parser2.parse_args(extras, namespace=args)
args.func(args)
else:
print('doing system with', args, extras)
样本运行:
0901:~/mypy$ python stack46667843.py -i info
('doing system with', Namespace(info='info'), [])
0901:~/mypy$ python stack46667843.py -i info extras for sys
('doing system with', Namespace(info='info'), ['extras', 'for', 'sys'])
0901:~/mypy$ python stack46667843.py -i info cmd1
('cmd1', Namespace(cmd='cmd1', func=<function cmd1 at 0xb74b025c>, info='info'))
0901:~/mypy$ python stack46667843.py -i info cmd2 -o out
('cmd2', Namespace(cmd='cmd2', func=<function cmd2 at 0xb719ebc4>, info='info', output='out'))
0901:~/mypy$
我正在使用 Python 2.7,我正在尝试使用 argparse 完成类似 shell 的行为。 我的问题,总的来说,我似乎无法在 Python 2.7 中找到一种方法来使用 argparse 的子解析器作为可选。 很难解释我的问题,所以我将描述我的程序需要什么。
程序有两种工作模式:
- 用给定的命令启动程序(每个命令都有自己的 附加参数)和附加参数将 运行 特定 任务。
- 在没有命令的情况下启动程序将启动一个类似于 shell 的程序,该程序可以接受一行参数并像处理 程序以给定行作为参数调用。
所以,例如,如果我的程序支持 'cmd1' 和 'cmd2' 命令,我可以像这样使用它:
python program.py cmd1 additional_args1
python program.py cmd2 additional_args2
或使用 shell 模式:
python program.py
cmd1 additional_args1
cmd2 additional_args2
quit
此外,我还希望我的程序能够采用将影响所有命令的可选全局参数。
为此我使用了 argparse(这是一个纯粹的例子):
parser = argparse.ArgumentParser(description="{} - Version {}".format(PROGRAM_NAME, PROGRAM_VERSION))
parser.add_argument("-i", "--info", help="Display more information")
subparsers = parser.add_subparsers()
parserCmd1 = subparsers.add_parser("cmd1", help="First Command")
parserCmd1.set_defaults(func=cmd1)
parserCmd2 = subparsers.add_parser("cmd2", help="Second Command")
parserCmd2.add_argument("-o", "--output", help="Redirect Output")
parserCmd2.set_defaults(func=cmd2)
所以我可以调用 cmd1(没有额外的参数)或 cmd2(有或没有 -o 标志)。对于这两者,我都可以添加标志 -i 以显示被调用命令的更多信息。
我的问题是我无法激活 shell 模式,因为我必须提供 cmd1 或 cmd2 作为参数(因为使用了强制性的子解析器)
限制:
- 我不能使用Python 3(我知道在那里可以很容易地完成)
- 由于全局可选参数,我无法检查是否没有参数来跳过 arg 解析。
- 我不想添加一个新的命令来调用shell,它必须是在根本没有提供任何命令的情况下
那么如何使用 argparse 和 python 2.7 实现这种行为?
关于 'optional' 子解析器主题的 bug/issue(带链接)。
https://bugs.python.org/issue29298
请注意,这有一个最近的拉取请求。
加上你的脚本和
args = parser.parse_args()
print(args)
结果是
1008:~/mypy$ python3 stack46667843.py
Namespace(info=None)
1009:~/mypy$ python2 stack46667843.py
usage: stack46667843.py [-h] [-i INFO] {cmd1,cmd2} ...
stack46667843.py: error: too few arguments
1009:~/mypy$ python2 stack46667843.py cmd1
Namespace(func=<function cmd1 at 0xb748825c>, info=None)
1011:~/mypy$ python3 stack46667843.py cmd1
Namespace(func=<function cmd1 at 0xb7134dac>, info=None)
我认为 'optional' 子解析器影响了 Py2 和 3 版本,但显然它没有。我将不得不查看代码来验证原因。
在这两种语言中,subparsers.required
是 False
。如果我将它设置为 true
subparsers.required=True
(并在subparsers定义中添加一个dest
),PY3错误信息为
1031:~/mypy$ python3 stack46667843.py
usage: stack46667843.py [-h] [-i INFO] {cmd1,cmd2} ...
stack46667843.py: error: the following arguments are required: cmd
所以这两个版本测试 required
参数的方式不同。 Py3注意required
属性; Py2(显然)使用较早的方法检查 positionals
列表是否为空。
在 parser._parse_known_args
.
Python2.7 包括
# if we didn't use all the Positional objects, there were too few
# arg strings supplied.
if positionals:
self.error(_('too few arguments'))
在检查 action.required
的迭代之前。这就是捕捉丢失的 cmd
并说 too few arguments
.
因此,需要编辑您的 argparse.py
并删除该块,使其与 Py3 版本的相应部分相匹配。
另一个想法是使用 2 阶段解析。一个处理 'globals',返回它无法处理的字符串。然后有条件地使用子解析器处理额外内容。
import argparse
def cmd1(args):
print('cmd1', args)
def cmd2(args):
print('cmd2', args)
parser1 = argparse.ArgumentParser()
parser1.add_argument("-i", "--info", help="Display more information")
parser2 = argparse.ArgumentParser()
subparsers = parser2.add_subparsers(dest='cmd')
parserCmd1 = subparsers.add_parser("cmd1", help="First Command")
parserCmd1.set_defaults(func=cmd1)
parserCmd2 = subparsers.add_parser("cmd2", help="Second Command")
parserCmd2.add_argument("-o", "--output", help="Redirect Output")
parserCmd2.set_defaults(func=cmd2)
args, extras = parser1.parse_known_args()
if len(extras)>0 and extras[0] in ['cmd1','cmd2']:
args = parser2.parse_args(extras, namespace=args)
args.func(args)
else:
print('doing system with', args, extras)
样本运行:
0901:~/mypy$ python stack46667843.py -i info
('doing system with', Namespace(info='info'), [])
0901:~/mypy$ python stack46667843.py -i info extras for sys
('doing system with', Namespace(info='info'), ['extras', 'for', 'sys'])
0901:~/mypy$ python stack46667843.py -i info cmd1
('cmd1', Namespace(cmd='cmd1', func=<function cmd1 at 0xb74b025c>, info='info'))
0901:~/mypy$ python stack46667843.py -i info cmd2 -o out
('cmd2', Namespace(cmd='cmd2', func=<function cmd2 at 0xb719ebc4>, info='info', output='out'))
0901:~/mypy$