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

Mat*_*ner 31 python parsing command-line-arguments argparse subcommand

如何使用默认子命令,或处理没有使用子命令的情况argparse

import argparse

a = argparse.ArgumentParser()
b = a.add_subparsers()
b.add_parser('hi')
a.parse_args()
Run Code Online (Sandbox Code Playgroud)

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

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

Ant*_*hon 18

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

更通用的解决方案是添加一个方法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()
Run Code Online (Sandbox Code Playgroud)

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

python3 default_subcommand.py
Run Code Online (Sandbox Code Playgroud)

python3 default_subcommand.py hi
Run Code Online (Sandbox Code Playgroud)

具有相同的效果.

允许选择新的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)
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

$ python test_parser.py hi
Namespace(cmd='hi')
$ python test_parser.py 
Namespace(cmd='hey')
Run Code Online (Sandbox Code Playgroud)


Mat*_*ner 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...
Run Code Online (Sandbox Code Playgroud)


Tho*_*ger 5

这是添加set_default_subparser方法的更好方法:

class DefaultSubcommandArgParse(argparse.ArgumentParser):
    __default_subparser = None

    def set_default_subparser(self, name):
        self.__default_subparser = name

    def _parse_known_args(self, arg_strings, *args, **kwargs):
        in_args = set(arg_strings)
        d_sp = self.__default_subparser
        if d_sp is not None and not {'-h', '--help'}.intersection(in_args):
            for x in self._subparsers._actions:
                subparser_found = (
                    isinstance(x, argparse._SubParsersAction) and
                    in_args.intersection(x._name_parser_map.keys())
                )
                if subparser_found:
                    break
            else:
                # insert default in first position, this implies no
                # global options without a sub_parsers specified
                arg_strings = [d_sp] + arg_strings
        return super(DefaultSubcommandArgParse, self)._parse_known_args(
            arg_strings, *args, **kwargs
        )
Run Code Online (Sandbox Code Playgroud)

  • 这个想法是不使用monkeypatch argparse.ArgumentParser,而是将其子类化 (2认同)