我想让我的程序通过子解析器处理命令(
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')
有没有办法做到这一点,或者子解析器与其他任意位置参数不兼容?
根据您的示例,可选 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:]
。