2013-09-04 13 views
7

Muszę zabraknąć czegoś oczywistego. Celem jest użycie argparse z pierwszym wymaganym parametrem, opcjonalnie drugim opcjonalnym i dowolnymi pozostałymi parametrami.Python argparse: Połącz opcjonalne parametry z nargs = argparse.REMAINDER

Aby pokazać problem, zrobiłem dwa parsery testowe; jedyną różnicą między nimi jest używanie nargs = argparse.REMAINDER w jednym i nargs = "*" w drugim.

def doParser1(argsin): 
    parser = argparse.ArgumentParser(description='Parser demo.') 
    parser.add_argument('req1', help='first required parameter') 
    parser.add_argument('--opt1', help='first optional parameter') 
    parser.add_argument('leftovers', nargs=argparse.REMAINDER, 
        help='all the other parameters') 
    argsout = parser.parse_args(args=argsin) 
    print argsout 
    return argsout 

def doParser2(argsin): 
    parser = argparse.ArgumentParser(description='Parser demo.') 
    parser.add_argument('req1', help='first required parameter') 
    parser.add_argument('--opt1', help='first optional parameter') 
    parser.add_argument('leftovers', nargs='*', 
        help='all the other parameters') 
    argsout = parser.parse_args(args=argsin) 
    print argsout 
    return argsout 

Jeśli nie ma żadnych dodatkowych parametrów, parser2 działa. To jest wejście następnie parser1 i parsera 1:

input: ['req1value', '--opt1', 'opt1value'] 
Namespace(leftovers=['--opt1', 'opt1value'], opt1=None, req1='req1value') 
Namespace(leftovers=None, opt1='opt1value', req1='req1value') 

Jeśli istnieją dodatkowe parametry, wartość OPT1 pominięcia w parser1 i parser2 tylko pogubi:

input: ['req1value', '--opt1', 'opt1value', 'r1', 'r2'] 
Namespace(leftovers=['--opt1', 'opt1value', 'r1', 'r2'], opt1=None, req1='req1value') 
usage: py-argparse.py [-h] [--opt1 OPT1] 
        [-leftovers [LEFTOVERS [LEFTOVERS ...]]] 
        req1 
py-argparse.py: error: unrecognized arguments: r1 r2 

Oczekiwany wynik powinien być:

Namespace(leftovers=['r1', 'r2'], opt1='opt1value', req1='req1value') 

Wygląda na to, że powinien to być prosty przypadek, a to, co jest tutaj, zostało uproszczone z tego, co naprawdę próbuję zrobić. Próbowałem robić resztki opcjonalnie, dodając wiele innych opcji, ale nic nie działa lepiej.

Każda pomoc zostanie doceniona.

Odpowiedz

3

--opt1 musi pojawić się przed argumentami "bez nazwy". Twoje sprawy prawdziwy test powinien być:

['--opt1', 'opt1value', 'req1value'] 

i

['--opt1', 'opt1value', 'req1value', 'r1', 'r2'] 
+0

Wiedziałem, że musi to być coś prostego. Nie widziałem tego w dokumentach nigdzie, ale działa to dla nargs = argparse.REMAINDER i to wszystko, czego potrzebuję. Dzięki. – OPunWide

4

można użyć parse_known_args:

import argparse 
parser = argparse.ArgumentParser(description='Parser demo.') 
parser.add_argument('req1', help='first required parameter') 
parser.add_argument('--opt1', help='first optional parameter') 

args, leftovers = parser.parse_known_args(['req1value', '--opt1', 'opt1value']) 
print(args, leftovers) 
# (Namespace(opt1='opt1value', req1='req1value'), []) 

args, leftovers = parser.parse_known_args(['req1value', '--opt1', 'opt1value', 'r1', 'r2']) 
print(args, leftovers) 
# (Namespace(opt1='opt1value', req1='req1value'), ['r1', 'r2']) 
+0

Złożę to na później. Druga odpowiedź pasowała do tego, czego potrzebuję. Wygląda na to, że to rozwiązanie nie wymaga opcji jako pierwszej, prawda? – OPunWide

+0

Dobrze. To zaakceptuje argumenty w dowolnej kolejności. Te, które nie pasują, kończą się na "resztkach". – unutbu

+0

Ładne rozwiązanie. Jednak poprzednia * pozostałość * nie będzie już pokazywana w dokumentacji/pomocy. – hitzg

2

inter mieszanie positionals I OPCJE jest trudne, gdy jeden lub więcej z positionals jest typu "zero lub więcej" (? * REMAINDER). Prostym rozwiązaniem jest nie mieszanie ich - najpierw podaj opcje, potem wszystkie pozycje.

Oto co się dzieje:

input: ['req1value', '--opt1', 'opt1value'] 
Namespace(leftovers=['--opt1', 'opt1value'], opt1=None, req1='req1value') 

powodu napisu req1value parser najpierw analizuje dla positionals. req1 chce 1 ciąg, leftovers chwyta wszystko inne, w tym --opt1.

Namespace(leftovers=None, opt1='opt1value', req1='req1value') 

Z *leftovers jest zadowolony z [], bez tekstu, stąd None (faktycznie mam []). --opt1 jest analizowany jako opcjonalny.

input: ['req1value', '--opt1', 'opt1value', 'r1', 'r2'] 
... 
py-argparse.py: error: unrecognized arguments: r1 r2 

Jak poprzednio *leftovers jest ustawiony na []. Przetworzono -opt1. Ale teraz są 2 struny bez miejsca na ich umieszczenie. Zamierzałeś je wpisać w leftovers, ale to już było używane. Jeśli leftovers byłby +, to zabrałoby je tak, jak zamierzałeś.

Klucz polega na tym, że podczas próby przeanalizowania pierwszego położenia próbuje również przeanalizować wszystkie pozycje, które może. Na jednym poziomie parse_args wykonuje re.match('(A)(A*)','AOA') produkujące grupy ('A', '').

Istnieją 2 proponowane łaty, które zajmują się tym problemem. Jeden używa 2 kroku parse_known_args, aby umożliwić całkowite wymieszanie opcji i pozycji. Jest to rodzaj zachowania, którego użytkownicy mogą oczekiwać.

Druga łata próbuje opóźnić obsługę pozycji, które mogą przyjmować 0 łańcuchy argumentów .

Powiązane problemy