Как иметь аргументы подпарасера в отдельном пространстве имен с argparse?
У меня есть следующий тест-код
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", default = 0, type=int)
subparsers = parser.add_subparsers(dest = "parser_name")
parser_lan = subparsers.add_parser('car')
parser_lan.add_argument("--boo")
parser_lan.add_argument("--foo")
parser_serial = subparsers.add_parser('bus')
parser_serial.add_argument("--fun")
print parser.parse_args()
который определяет два подпарсера, имеющих различный набор аргументов. Когда я называю тест-код как
tester.py --verbose 3 car --boo 1 --foo 2
Я получаю ожидаемый результат
Namespace(boo='1', foo='2', parser_name='car', verbose=3)
Вместо этого я хочу получить значения из каждого подпапера в отдельном пространстве имен или в dict, что-то вроде
Namespace(subparseargs={boo:'1', foo:'2'}, parser_name='car', verbose=3)
так что аргументы каждого подпаратера логически отделены от аргументов основного синтаксического анализатора (как verbose
в этом примере).
Как я могу добиться этого, с аргументами для каждого подпаратера в том же пространстве имен (subparseargs
в примере).
4 ответа
Я начал разрабатывать другой подход (но похожий на предложение Антона) и придумал гораздо более короткий код. Тем не менее, я не уверен, что мой подход является общим решением проблемы.
Подобно тому, что предлагает Anthon, я определяю новый метод, который создает список аргументов "верхнего уровня", которые хранятся в args
в то время как все остальные аргументы возвращаются в качестве дополнительного словаря:
class MyArgumentParser(argparse.ArgumentParser):
def parse_subargs(self, *args, **kw):
# parse as usual
args = argparse.ArgumentParser.parse_args(self, *args, **kw)
# extract the destination names for top-level arguments
topdest = [action.dest for action in parser._actions]
# loop over all arguments given in args
subargs = {}
for key, value in args.__dict__.items():
# if sub-parser argument found ...
if key not in topdest:
# ... remove from args and add to dictionary
delattr(args,key)
subargs[key] = value
return args, subargs
Комментарии к этому подходу приветствуются, особенно любые лазейки, которые я пропустил.
Вам нужно войти в недра argparse
немного, но изменение вашего сценария на следующее должно помочь:
import argparse
from argparse import _HelpAction, _SubParsersAction
class MyArgumentParser(argparse.ArgumentParser):
def parse_args(self, *args, **kw):
res = argparse.ArgumentParser.parse_args(self, *args, **kw)
from argparse import _HelpAction, _SubParsersAction
for x in parser._subparsers._actions:
if not isinstance(x, _SubParsersAction):
continue
v = x.choices[res.parser_name] # select the subparser name
subparseargs = {}
for x1 in v._optionals._actions: # loop over the actions
if isinstance(x1, _HelpAction): # skip help
continue
n = x1.dest
if hasattr(res, n): # pop the argument
subparseargs[n] = getattr(res, n)
delattr(res, n)
res.subparseargs = subparseargs
return res
parser = MyArgumentParser()
parser.add_argument("--verbose", default = 0, type=int)
subparsers = parser.add_subparsers(dest = "parser_name")
parser_lan = subparsers.add_parser('car')
parser_lan.add_argument("--boo")
parser_lan.add_argument("--foo")
parser_serial = subparsers.add_parser('bus')
parser_serial.add_argument("--fun")
print parser.parse_args()
Или вручную вы можете проанализировать аргументы и создать dict с подробностями:
# parse args
args = parser.parse_args()
args_dict = {}
for group in parser._action_groups:
# split into groups based on title
args_dict[group.title] = {}
for arg in group._group_actions:
if hasattr(args, arg.dest):
args_dict[group.title][arg.dest] = getattr(args, arg.dest)
# or args_dict[arg.dest] = getattr(args, arg.dest)
delattr(args, arg.dest)
# add remaining items into subparser options
args_dict["subparser"] |= vars(args)
return args_dict
Чтобы отфильтровать аргументы субпарсеров, вы можете использоватьparse_known_args
метод:
car_subparser_args = parser_lan.parse_known_args()[0]
bus_subparser_args = parser_serial.parse_known_args()[0]
print(vars(car_subparser_args)) # -> {'boo': '1', 'foo': '2'}
print(vars(bus_subparser_args)) # -> {'fun': None}