有没有办法为 argparse 提供可选参数和子解析器

问题描述 投票:0回答:1

我想让我的程序通过子解析器处理命令(

gui
daemon
...),但也处理一个可选的位置
url
参数。仅当操作系统调用我的程序时才会使用可选参数,因为用户单击了与我的程序关联的某些 URI。

这是我想要实现的目标的简化示例:

import argparse


parser = argparse.ArgumentParser()


parser.add_argument("url", nargs="?")

# global options
parser.add_argument("--password")
parser.add_argument("--verbose", action="store_true")

# subparser
subparsers = parser.add_subparsers(dest="cmd", help='sub-command help')

gui_parser = subparsers.add_parser("gui")
gui_parser.add_argument("--testnet", action="store_true")


daemon_parser = subparsers.add_parser("daemon")
daemon_parser.add_argument("subcommand", nargs="?", help="start, stop, status or commands added by plugins")
daemon_parser.add_argument("subargs", nargs="*",  metavar="arg", help="additional arguments (used by plugins)")


url = 'ecash:sdfqsdf?amount=1337.42&message="monthly payment to supplier #42"'

args = parser.parse_args([url])
print(args.url, args.cmd, args.verbose, args.password)

args = parser.parse_args(["--verbose", url])
print(args.url, args.cmd, args.verbose, args.password)

args = parser.parse_args(["--verbose", "--password", "123", "gui", "--testnet"])
print(args.url, args.password, args.cmd, args.testnet)

args = parser.parse_args(["daemon", "start"])
print(args.url, args.password, args.cmd, args.subcommand)

args = parser.parse_args(["daemon", "show_label", "id00021"])
print(args.url, args.password, args.cmd, args.subcommand, args.subargs)

不幸的是,可选的

url
参数似乎不起作用:

$ python test.py
usage: test.py [-h] [--password PASSWORD] [--verbose] [url] {gui,daemon} ...
test.py: error: argument cmd: invalid choice: 'ecash:sdfqsdf?amount=1337.42&message="monthly payment to supplier #42"' (choose from 'gui', 'daemon')

有没有办法做到这一点,或者子解析器与其他任意位置参数不兼容?

python argparse
1个回答
0
投票

根据您的示例,可选 URL 和子命令似乎是互斥的。这是解决此问题的一种方法:我将分两遍解析命令行参数。第一个只会解析出 url 或子命令。下一次将解析剩余部分。

#!/usr/bin/env python3
import argparse


def parse_command_line(command_line=None):
    # Pass 1: parse --password, --verbose, (url or cmd)
    parser = argparse.ArgumentParser()
    parser.add_argument("--password")
    parser.add_argument("--verbose", action="store_true")
    parser.add_argument("cmd")
    options, remainder = parser.parse_known_args(command_line)

    # Pass 2: parse the remainder base on gui, daemon, or URL
    if options.cmd == "gui":
        parser = argparse.ArgumentParser()
        parser.add_argument("--testnet", action="store_true")
        parser.parse_args(remainder, options)
    elif options.cmd == "daemon":
        parser = argparse.ArgumentParser()
        parser.add_argument(
            "subcommand",
            nargs="?",
            help="start, stop, status or commands added by plugins",
        )
        parser.add_argument(
            "subargs",
            nargs="*",
            metavar="arg",
            help="additional arguments (used by plugins)",
        )
        parser.parse_args(remainder, options)
    else:
        # Assume cmd is a URL
        options.url = options.cmd
        options.cmd = "url"
    return options


def tryout(command_line):
    print(f"\n# {command_line}")
    options = parse_command_line(command_line)
    print(options)


url = 'ecash:sdfqsdf?amount=1337.42&message="monthly payment to supplier #42"'
tryout([url])
tryout(["--verbose", url])
tryout(["--verbose", "--password", "123", "gui", "--testnet"])
tryout(["daemon", "start"])
tryout(["daemon", "show_label", "id00021"])

输出:

# ['ecash:sdfqsdf?amount=1337.42&message="monthly payment to supplier #42"']
Namespace(password=None, verbose=False, cmd='url', url='ecash:sdfqsdf?amount=1337.42&message="monthly payment to supplier #42"')

# ['--verbose', 'ecash:sdfqsdf?amount=1337.42&message="monthly payment to supplier #42"']
Namespace(password=None, verbose=True, cmd='url', url='ecash:sdfqsdf?amount=1337.42&message="monthly payment to supplier #42"')

# ['--verbose', '--password', '123', 'gui', '--testnet']
Namespace(password='123', verbose=True, cmd='gui', testnet=True)

# ['daemon', 'start']
Namespace(password=None, verbose=False, cmd='daemon', subcommand='start', subargs=[])

# ['daemon', 'show_label', 'id00021']
Namespace(password=None, verbose=False, cmd='daemon', subcommand='show_label', subargs=['id00021'])

注释

  • 调用
    parse_command_line
    时,如果调用者未指定
    command_line
    ,则将使用
    sys.argv[1:]
  • 如果 URL 与子命令(gui、daemon)不互斥,那么您可能需要将其设为“--url”而不是“url”
© www.soinside.com 2019 - 2024. All rights reserved.