默认子命令,或者不使用argparse处理子命令

时间:2011-06-15 23:24:34

标签: python parsing command-line-arguments argparse subcommand

如何使用默认sub-command,或处理使用argparse没有给出子命令的情况?

import argparse

a = argparse.ArgumentParser()
b = a.add_subparsers()
b.add_parser('hi')
a.parse_args()

这里我想要一个要选择的命令,或者只根据下一个最高级别的解析器(在这种情况下是顶级解析器)处理的参数。

joiner@X:~/src> python3 default_subcommand.py
usage: default_subcommand.py [-h] {hi} ...
default_subcommand.py: error: too few arguments

9 个答案:

答案 0 :(得分:18)

在Python 3.2(和2.7)上,您将得到该错误,但不会在3.3和3.4(无响应)上。因此,在3.3 / 3.4上,您可以测试parsed_args为空Namespace

更通用的解决方案是添加方法set_default_subparser()(取自ruamel.std.argparse包)并在parse_args()之前调用该方法:

import argparse
import sys

def set_default_subparser(self, name, args=None, positional_args=0):
    """default subparser selection. Call after setup, just before parse_args()
    name: is the name of the subparser to call by default
    args: if set is the argument list handed to parse_args()

    , tested with 2.7, 3.2, 3.3, 3.4
    it works with 2.6 assuming argparse is installed
    """
    subparser_found = False
    for arg in sys.argv[1:]:
        if arg in ['-h', '--help']:  # global help if no subparser
            break
    else:
        for x in self._subparsers._actions:
            if not isinstance(x, argparse._SubParsersAction):
                continue
            for sp_name in x._name_parser_map.keys():
                if sp_name in sys.argv[1:]:
                    subparser_found = True
        if not subparser_found:
            # insert default in last position before global positional
            # arguments, this implies no global options are specified after
            # first positional argument
            if args is None:
                sys.argv.insert(len(sys.argv) - positional_args, name)
            else:
                args.insert(len(args) - positional_args, name)

argparse.ArgumentParser.set_default_subparser = set_default_subparser

def do_hi():
    print('inside hi')

a = argparse.ArgumentParser()
b = a.add_subparsers()
sp = b.add_parser('hi')
sp.set_defaults(func=do_hi)

a.set_default_subparser('hi')
parsed_args = a.parse_args()

if hasattr(parsed_args, 'func'):
    parsed_args.func()

这适用于2.6(如果从PyPI安装argparse),2.7,3.2,3.3,3.4。并允许你做两个

python3 default_subcommand.py

python3 default_subcommand.py hi

具有相同的效果。

允许选择新的subparser作为默认值,而不是现有的一个。

代码的第一个版本允许将以前定义的子分析符之一设置为默认子分类符。以下修改允许添加新的默认子分析程序,然后可以在用户未选择子分析程序时使用该子分析程序专门处理该情况(代码中标记的不同行)

def set_default_subparser(self, name, args=None, positional_args=0):
    """default subparser selection. Call after setup, just before parse_args()
    name: is the name of the subparser to call by default
    args: if set is the argument list handed to parse_args()

    , tested with 2.7, 3.2, 3.3, 3.4
    it works with 2.6 assuming argparse is installed
    """
    subparser_found = False
    existing_default = False # check if default parser previously defined
    for arg in sys.argv[1:]:
        if arg in ['-h', '--help']:  # global help if no subparser
            break
    else:
        for x in self._subparsers._actions:
            if not isinstance(x, argparse._SubParsersAction):
                continue
            for sp_name in x._name_parser_map.keys():
                if sp_name in sys.argv[1:]:
                    subparser_found = True
                if sp_name == name: # check existance of default parser
                    existing_default = True
        if not subparser_found:
            # If the default subparser is not among the existing ones,
            # create a new parser.
            # As this is called just before 'parse_args', the default
            # parser created here will not pollute the help output.

            if not existing_default:
                for x in self._subparsers._actions:
                    if not isinstance(x, argparse._SubParsersAction):
                        continue
                    x.add_parser(name)
                    break # this works OK, but should I check further?

            # insert default in last position before global positional
            # arguments, this implies no global options are specified after
            # first positional argument
            if args is None:
                sys.argv.insert(len(sys.argv) - positional_args, name)
            else:
                args.insert(len(args) - positional_args, name)

argparse.ArgumentParser.set_default_subparser = set_default_subparser

a = argparse.ArgumentParser()
b = a.add_subparsers(dest ='cmd')
sp = b.add_parser('hi')
sp2 = b.add_parser('hai')

a.set_default_subparser('hey')
parsed_args = a.parse_args()

print(parsed_args)

“默认”选项仍未显示在帮助中:

python test_parser.py -h
usage: test_parser.py [-h] {hi,hai} ...

positional arguments:
  {hi,hai}

optional arguments:
  -h, --help  show this help message and exit

但是,现在可以区分和单独处理调用提供的子分析符之一,并在没有提供参数时调用默认子分析符:

$ python test_parser.py hi
Namespace(cmd='hi')
$ python test_parser.py 
Namespace(cmd='hey')

答案 1 :(得分:7)

我最终自己偶然发现了解决方案。

如果该命令是可选的,那么会使命令成为选项。在我原来的解析器配置中,我有一个package命令可以采取一系列可能的步骤,或者如果没有给出,它将执行所有步骤。这使得该步骤成为一个选择:

parser = argparse.ArgumentParser()

command_parser = subparsers.add_parser('command')
command_parser.add_argument('--step', choices=['prepare', 'configure', 'compile', 'stage', 'package'])

...other command parsers

parsed_args = parser.parse_args()

if parsed_args.step is None:
    do all the steps...

答案 2 :(得分:3)

这是添加perl -wMstrict -MData::Dumper -E 'my %h; my $x=1; $h{$x}->{y}=3; print Dumper \%h;' $VAR1 = { '1' => { 'y' => 3 } }; 方法的更好方法:

set_default_subparser

答案 3 :(得分:2)

您正在寻找的是dest的{​​{1}}参数:

警告:适用于Python 3.4,但不适用于2.7

add_subparsers

现在您可以使用import argparse parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest='cmd') parser_hi = subparsers.add_parser('hi') parser.parse_args([]) # Namespace(cmd=None)

的值
cmd

答案 4 :(得分:0)

你可以添加一个带有默认值的参数,当我认为没有设置时,将使用该参数。

请参阅:http://docs.python.org/dev/library/argparse.html#default

编辑:

抱歉,我读得你的问题有点快。

我认为你不会直接通过argparse做你想做的事。但你可以检查sys.argv的长度,如果它的长度是1(只有脚本名称),那么你可以手动传递默认参数进行解析,做这样的事情:

import argparse

a = argparse.ArgumentParser()
b = a.add_subparsers()
b.add_parser('hi')

if len(sys.argv) == 1:
   a.parse_args(['hi'])
else:
   a.parse_args()

我认为应该做你想做的事,但我同意开箱即用会很好。

答案 5 :(得分:0)

在python 2.7中,您可以在子类中覆盖错误行为(可惜没有更好的方法来区分错误):

import argparse

class ExceptionArgParser(argparse.ArgumentParser):

    def error(self, message):
        if "invalid choice" in message:
            # throw exception (of your choice) to catch
            raise RuntimeError(message)
        else:
            # restore normal behaviour
            super(ExceptionArgParser, self).error(message)


parser = ExceptionArgParser()
subparsers = parser.add_subparsers(title='Modes', dest='mode')

default_parser = subparsers.add_parser('default')
default_parser.add_argument('a', nargs="+")

other_parser = subparsers.add_parser('other')
other_parser.add_argument('b', nargs="+")

try:
    args = parser.parse_args()
except RuntimeError:
    args = default_parser.parse_args()
    # force the mode into namespace
    setattr(args, 'mode', 'default') 

print args

答案 6 :(得分:0)

您可以在主解析器上复制特定子解析器的默认操作,从而有效地使其成为默认解析器。

import argparse
p = argparse.ArgumentParser()
sp = p.add_subparsers()

a = sp.add_parser('a')
a.set_defaults(func=do_a)

b = sp.add_parser('b')
b.set_defaults(func=do_b)

p.set_defaults(func=do_b)
args = p.parse_args()

if args.func:
    args.func()
else:
    parser.print_help()

不适用于add_subparsers(required=True),这就是if args.func在此处的原因。

答案 7 :(得分:0)

对于我来说,我发现最简单的做法是在parse_args()为空时,将子命令名称显式提供给argv

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help='commands')

runParser = subparsers.add_parser('run', help='[DEFAULT ACTION]')

altParser = subparsers.add_parser('alt', help='Alternate command')
altParser.add_argument('alt_val', type=str, help='Value required for alt command.')

# Here's my shortcut: If `argv` only contains the script name,
# manually inject our "default" command.
args = parser.parse_args(['run'] if len(sys.argv) == 1 else None)
print args

示例运行:

$ ./test.py 
Namespace()

$ ./test.py alt blah
Namespace(alt_val='blah')

$ ./test.py blah
usage: test.py [-h] {run,alt} ...
test.py: error: invalid choice: 'blah' (choose from 'run', 'alt')

答案 8 :(得分:-1)

供以后参考:

...
b = a.add_subparsers(dest='cmd')
b.set_defaults(cmd='hey')  # <-- this makes hey as default

b.add_parser('hi')

所以,这两个是相同的:

  • python main.py嘿
  • python main.py