2011-01-22 2 views
17

Если у меня есть аргументы '-a', '-b', '-c', '-d', с функцией add_mutually_exclusive_group() моя программа должна будет использовать только один из них. Есть ли способ объединить это, так что программа будет принимать только '-a 999 -b 999' или '-c 999 -d 999'?Поддерживает ли argparse (python) взаимоисключающие группы аргументов?

Edit: добавить простую программу для большей ясности:

>>> parser = argparse.ArgumentParser() 
>>> group = parser.add_mutually_exclusive_group() 
>>> group.add_argument('-a') 
>>> group.add_argument('-b') 
>>> group.add_argument('-c') 
>>> group.add_argument('-d') 

Тогда можно назвать только ./app.py -a | ./app.py -b | ./app.py -c | ./app.py -d. Возможно ли иметь группу argparse исключающих групп, чтобы можно было назвать только ./app.py -a .. -b .. | ./app.py -c .. -d ..?

ответ

6

EDIT: Ничего. Потому что argparse делает ужасный выбор при создании опции при вызове group.add_argument. Это был бы не мой выбор дизайна. Если вы отчаянно для этой функции, вы можете попробовать сделать это с ConflictsOptionParser:

# exclusivegroups.py 
import conflictsparse 

parser = conflictsparse.ConflictsOptionParser() 
a_opt = parser.add_option('-a') 
b_opt = parser.add_option('-b') 
c_opt = parser.add_option('-c') 
d_opt = parser.add_option('-d') 

import itertools 
compatible_opts1 = (a_opt, b_opt) 
compatible_opts2 = (c_opt, d_opt) 
exclusives = itertools.product(compatible_opts1, compatible_opts2) 
for exclusive_grp in exclusives: 
    parser.register_conflict(exclusive_grp) 


opts, args = parser.parse_args() 
print "opts: ", opts 
print "args: ", args 

Таким образом, когда мы вызываем его, мы можем видеть, что мы получим желаемый эффект.

$ python exclusivegroups.py -a 1 -b 2 
opts: {'a': '1', 'c': None, 'b': '2', 'd': None} 
args: [] 
$ python exclusivegroups.py -c 3 -d 2 
opts: {'a': None, 'c': '3', 'b': None, 'd': '2'} 
args: [] 
$ python exclusivegroups.py -a 1 -b 2 -c 3 
Usage: exclusivegroups.py [options] 

exclusivegroups.py: error: -b, -c are incompatible options. 

предупреждающее сообщение не сообщает вам, что оба '-a' и '-b' несовместимы с '-c', однако более соответствующее сообщение об ошибке может быть сделан. Более старый, неправильный ответ ниже.

СТАРШЕ EDIT:[Это изменение не так, хотя это не будет просто идеальный мир, если argparse работал этот путь?] Мой предыдущий ответ был на самом деле неправильно, вы должны быть в состоянии сделать это с argparse указав одну группу на взаимоисключающие опции. Мы можем использовать itertools для обобщения процесса. И сделайте так, чтобы мы не вводили все комбинации явно:

import itertools 
compatible_opts1 = ('-a', '-b') 
compatible_opts2 = ('-c', '-d') 
exclusives = itertools.product(compatible_opts1, compatible_opts2) 
for exclusive_grp in exclusives: 
    group = parser.add_mutually_exclusive_group() 
    group.add_argument(exclusive_grp[0]) 
    group.add_argument(exclusive_grp[1]) 
+1

http://bugs.python.org/issue10984 имеет патч, который позволяет помещать аргумент в более чем одну взаимоисключающую группу. Это легко изменить. Использование более значимого использования с перекрывающимися группами более активно. – hpaulj

5

Просто наткнулся на эту проблему сам. Из моего чтения документов argparse, похоже, нет простого способа добиться этого в argparse. Я рассмотрел использование parse_known_args, но вскоре это приведет к написанию специальной версии argparse ;-)

Возможно, отчет об ошибке в порядке. В то же время, если вы хотите сделать свой пользователь небольшим дополнительным набором текста, вы можете подделать его с помощью подгрупп (например, как работают аргументы git и svn), например.

Не идеальный, но по крайней мере он дает вам хорошее от argparse без особого уродливого хакера. Я закончил работу с коммутаторами и просто использовал операции subparser с необходимыми субаргументами.

Смежные вопросы