使用 Python 的 `argh` 时如何收集未指定的命令行选项?

How can I collect unspecified command line options when using Python's `argh`?

使用 Python CLI 库 argh 我想编写一个包装器工具。这个包装器工具假设读取两个选项 -a-b 并将所有其他选项传递给一个函数(然后通过 subprocess 使用剩余选项调用包装的 UNIX 工具).

我已经试验过 dispatch 的参数 skip_unknown_args:

def wrapper(a=True, b=False):
    print("Enter wrapper")
    # 1. process a and b 
    # 2. call_unix_tool(left-over-args)

if __name__ == '__main__':
    parser = argh.ArghParser()
    argh.set_default_command(parser, wrapper)
    argh.dispatch(parser, skip_unknown_args=True)

但是程序在遇到未知选项时仍然退出,并没有按需要进入函数wrapper。此外,我不知道 unknown/skipped 参数存储在哪里,以便我可以将它们传递给 UNIX 工具。

如何让 argh 使用跳过的参数进入 wrapper

我认为这是一个错误。

skip_unknown_args=True时,这里namespace_obj是一个元组,有一个命名空间对象和剩余的参数:

(Pdb) p namespace_obj
    (ArghNamespace(_functions_stack=[<function wrapper at 0x105cb5e18>], a=False, b=True), ['-c'])

底层 _get_function_from_namespace_obj 需要一元:

154     function = _get_function_from_namespace_obj(namespace_obj)
...
191     if isinstance(namespace_obj, ArghNamespace):

我检查了its coressponding issue and unittest,不知道作者期望的合法行为是什么,也在那里发表了评论。

为什么不直接使用argparse

您不能使用 skip_unknown_args=True 执行此操作,因为 argh 库在使用该选项时似乎表现不佳。但是,您可以提供自己的解析器 class,它将未知参数注入普通命名空间:

class ArghParserWithUnknownArgs(argh.ArghParser):
    def parse_args(self, args=None, namespace=None):
        namespace = namespace or ArghNamespace()
        (namespace_obj, unknown_args) = super(ArghParserWithUnknownArgs, self).parse_known_args(args=args, namespace=namespace)
        namespace_obj.__dict__['unknown_args'] = unknown_args
        return namespace_obj

请注意,此 class' parse_args 方法调用 ArgParserparse_known_args方法!

有了这个 class 定义,您可以按照以下方式编写包装代码:

def wrapper(a=True, b=False, unknown_args={}):
    print("a = %s, b = %s" % (a,b))
    print("unknown_args = %s" % unknown_args)

if __name__ == '__main__':
    parser = ArghParserWithUnknownArgs()
    argh.set_default_command(parser, wrapper)
    argh.dispatch(parser)

在您的主函数 wrapper 中,您可以通过参数 unknown_args 访问所有未知参数,并将其传递给您的子进程命令

ps:为了保持帮助信息的整洁,用

修饰wrapper
@argh.arg('--unknown_args', help=argparse.SUPPRESS)

附录:我创建了一个增强版的解析器并将其编译成一个随时可用的模块。在 Github.

上找到它