Преобразувам помощната програма за инсталиране на Bash shell в Python 2.7 и трябва да внедря сложен CLI, така че да мога да анализирам десетки параметри (потенциално до ~150). Това са имена на променливи на клас Puppet в допълнение към дузина общи опции за внедряване, които са налични във версията на обвивката.
Въпреки това, след като започнах да добавям повече променливи, се сблъсках с няколко предизвикателства: 1. Трябва да групирам параметрите в отделни речници, така че опциите за внедряване да са отделени от променливите Puppet. Ако бъдат хвърлени в една и съща кофа, тогава ще трябва да напиша някаква логика, за да ги сортирам, потенциално преименувайки параметри и тогава сливането на речници няма да е тривиално. 2. Възможно е да има променливи със същото име, но принадлежащи към различен клас Puppet, така че реших, че подкомандите ще ми позволят да филтрирам какво отива къде и да избягвам сблъсъци на имена.
В момента внедрих анализиране на параметри чрез просто добавяне на множество анализатори:
parser = argparse.ArgumentParser(description='deployment parameters.')
env_select = parser.add_argument_group(None, 'Environment selection')
env_select.add_argument('-c', '--client_id', help='Client name to use.')
env_select.add_argument('-e', '--environment', help='Environment name to use.')
setup_type = parser.add_argument_group(None, 'What kind of setup should be done:')
setup_type.add_argument('-i', '--install', choices=ANSWERS, metavar='', action=StoreBool, help='Yy/Nn Do normal install and configuration')
# MORE setup options
...
args, unk = parser.parse_known_args()
config['deploy_cfg'].update(args.__dict__)
pup_class1_parser = argparse.ArgumentParser(description=None)
pup_class1 = pup_class1_parser.add_argument_group(None, 'Puppet variables')
pup_class1.add_argument('--ad_domain', help='AD/LDAP domain name.')
pup_class1.add_argument('--ad_host', help='AD/LDAP server name.')
# Rest of the parameters
args, unk = pup_class1_parser.parse_known_args()
config['pup_class1'] = dict({})
config['pup_class1'].update(args.__dict__)
# Same for class2, class3 and so on.
Проблемът с този подход е, че той не решава проблем 2. Също така първият анализатор използва опцията "-h" и останалите параметри не се показват в помощта.
Опитах се да използвам пример, избран като отговор но не успях да използвам и двете команди едновременно.
## This function takes the 'extra' attribute from global namespace and re-parses it to create separate namespaces for all other chained commands.
def parse_extra (parser, namespace):
namespaces = []
extra = namespace.extra
while extra:
n = parser.parse_args(extra)
extra = n.extra
namespaces.append(n)
return namespaces
pp = pprint.PrettyPrinter(indent=4)
argparser=argparse.ArgumentParser()
subparsers = argparser.add_subparsers(help='sub-command help', dest='subparser_name')
parser_a = subparsers.add_parser('command_a', help = "command_a help")
## Setup options for parser_a
parser_a.add_argument('--opt_a1', help='option a1')
parser_a.add_argument('--opt_a2', help='option a2')
parser_b = subparsers.add_parser('command_b', help = "command_b help")
## Setup options for parser_a
parser_b.add_argument('--opt_b1', help='option b1')
parser_b.add_argument('--opt_b2', help='option b2')
## Add nargs="*" for zero or more other commands
argparser.add_argument('extra', nargs = "*", help = 'Other commands')
namespace = argparser.parse_args()
pp.pprint(namespace)
extra_namespaces = parse_extra( argparser, namespace )
pp.pprint(extra_namespaces)
Резултатът ми е:
$ python argtest.py command_b --opt_b1 b1 --opt_b2 b2 command_a --opt_a1 a1
usage: argtest.py [-h] {command_a,command_b} ... [extra [extra ...]]
argtest.py: error: unrecognized arguments: command_a --opt_a1 a1
Същият резултат беше, когато се опитах да дефинирам родител с два дъщерни анализатора.
ВЪПРОСИ
- Мога ли по някакъв начин да използвам parser.add_argument_group за синтактичен анализ на аргумент или е само за групирането в разпечатката на помощта? Това ще реши проблем 1, без да пропусне страничен ефект от помощта. Предаването му като
parse_known_args(namespace=argument_group)
(ако правилно си спомням моите експерименти) получава всички променливи (това е добре), но също така получава всички обектни неща на Python в получения dict (това е лошо за hieradata YAML) - Какво ми липсва във втория пример, за да позволя да използвам множество подкоманди? Или това е невъзможно с argparse?
- Някакво друго предложение за групиране на променливи на командния ред? Разгледах Click, но не намерих никакви предимства пред стандартния argparse за моята задача.
Забележка: Аз съм системен администратор, а не програмист, така че бъдете внимателни към мен за кодирането на необектен стил. :)
Благодаря ти
РЕШЕНО Групирането на аргументи е разрешено чрез отговора, предложен от hpaulj.
import argparse
import pprint
parser = argparse.ArgumentParser()
group_list = ['group1', 'group2']
group1 = parser.add_argument_group('group1')
group1.add_argument('--test11', help="test11")
group1.add_argument('--test12', help="test12")
group2 = parser.add_argument_group('group2')
group2.add_argument('--test21', help="test21")
group2.add_argument('--test22', help="test22")
args = parser.parse_args()
pp = pprint.PrettyPrinter(indent=4)
d = dict({})
for group in parser._action_groups:
if group.title in group_list:
d[group.title]={a.dest:getattr(args,a.dest,None) for a in group._group_actions}
print "Parsed arguments"
pp.pprint(d)
Това ми дава желания резултат за проблем №1. докато нямам множество параметри с едно и също име. Решението може да изглежда грозно, но поне работи според очакванията.
python argtest4.py --test22 aa --test11 yy11 --test21 aaa21
Parsed arguments
{ 'group1': { 'test11': 'yy11', 'test12': None},
'group2': { 'test21': 'aaa21', 'test22': 'aa'}}