2013-04-23 8 views
33

Czy istnieje sposób, aby argparse rozpoznawał cokolwiek między dwoma cudzysłowami jako pojedynczy argument? Wydaje się zachować widząc kreski i przy założeniu, że jest to początek nowej opcjiNie można uzyskać argparse, aby przeczytać cytowany ciąg z myślnikami w nim?

mam coś takiego:

mainparser = argparse.ArgumentParser() 
subparsers = mainparser.add_subparsers(dest='subcommand') 
parser = subparsers.add_parser('queue') 
parser.add_argument('-env', '--extraEnvVars', type=str, 
         help='String of extra arguments to be passed to model.') 
...other arguments added to parser... 

Ale kiedy biegnę:

python Application.py queue -env "-s WHATEVER -e COOL STUFF" 

Daje mi:

Application.py queue: error: argument -env/--extraEnvVars: expected one argument 

Jeśli zostawię pierwszy kresk, działa to zupełnie dobrze, ale jest to kluczowe, że mogę przekazać ciąg znaków z myślnikami. Próbowałem ucieczki z \, co powoduje, że powiodło się, ale dodaje \ do ciągu argumentów Czy ktoś wie, jak to obejść? Zdarza się to niezależnie od tego, czy -s jest argumentem w analizatorze składni.

EDYCJA: Używam Pythona 2.7.

EDIT2:

python Application.py -env " -env" 

działa perfekcyjnie, ale

python Application.py -env "-env" 

nie.

EDIT3: Wygląda na to, że jest to błąd, o którym już się debatuje: http://www.gossamer-threads.com/lists/python/bugs/89529, http://python.6.x6.nabble.com/issue9334-argparse-does-not-accept-options-taking-arguments-beginning-with-dash-regression-from-optp-td578790.html. Jest tylko w 2.7, a nie w optparse.

EDIT4: Bieżący raport otwarty błąd jest: http://bugs.python.org/issue9334

+0

Jaka wersja Pythona używasz? – William

+0

Używam Pythona 2.7. – sfendell

+0

Działa to dobrze dla mnie w Pythonie 2.7. Czy masz skonfigurowane inne * argumenty *? –

Odpowiedz

9

Argument z odstępem można uruchomić jako bardzo proste obejście. Po prostu lstrip() opcja, aby przywrócić normalne, jeśli chcesz.

Albo, jeśli pierwszy "podargument" nie jest również prawidłowym argumentem dla pierwotnej funkcji, nie powinieneś niczego robić. To znaczy, jedynym powodem, dla którego python tst.py -e '-s hi -e blah' nie działa, ponieważ -s jest prawidłową opcją dla tst.py.

Również moduł optparse, obecnie przestarzały, działa bez żadnego problemu.

+0

Nie sądzę, aby tak się stało, ponieważ -s jest poprawną opcją dla akapitu. Próbowałem go z python Application.py queue -e "-notarealoption" i dostałem ten sam błąd. Lubię używać lstrip nieco lepiej niż zamień + z - jak sugerował SethMorton, ale naprawdę wydaje się, że powinien istnieć sposób cytowania ciągu tak, aby nic w nim nie było zastępowane/zmieniane/czytane przez argparse. – sfendell

+1

Och, naprawdę? Właśnie oparłem to przypuszczenie na moich krótkich testach. Zrobiłem scenariusz, który wziął jeden argument, '-a', i po prostu wysłał go' -a '-b hello'' i wszystko działało dobrze. Ale myślę, że używam innej wersji Pythona. – William

+1

Edytowałem moje oryginalne pytanie. Podobno jest to znany błąd w argparse w> 2.7 :(Zmieniłem sys.argv zanim zadzwoniłem do parsera.parse_args(), aby dodać obojętny znak na początku opcji -env, a następnie ją zdjąć. Jest hacky i niepythonicznie jak cholera, ale w końcu dostałem to, co chciałem. – sfendell

33

Updated odpowiedź:

można postawić znak równości, gdy nazywają go:

python Application.py -env="-env" 

Original odpowiedź:

Ja też miałem problemy z robieniem tego, co próbujesz zrobić, ale istnieje metoda obejścia tego problemu, która jest metodą parse_known_args. Umożliwi to wszystkim argumentom, które nie zostały zdefiniowane, przechodzenie przez analizator składni z założeniem, że użyjesz ich do podprocesu. Wady polegają na tym, że nie otrzymasz zgłoszeń błędów ze złymi argumentami, a będziesz musiał upewnić się, że nie ma kolizji pomiędzy opcjami i opcjami twojego podprocesu.

Innym rozwiązaniem mogłoby być zmusić użytkownika do korzystania z plusa zamiast minusa:

python Application.py -e "+s WHATEVER +e COOL STUFF" 

a następnie zmienić „+”, aby - przed przekazaniem do podproces w przetwórstwie postu „”.

+1

Nie sądzę, że parse_known_args mi pomaga. Nie chcę w ogóle czytać argumentów w cytatach; Chciałbym, aby cytowany ciąg był przekazywany jako pojedynczy obiekt do -env. Zastanawiam się nad pójściem po przetwarzaniu trasy, a ja prawdopodobnie, jeśli nie dostanę lepszej odpowiedzi stąd, ale czuje się zjadliwe, a to oznacza, że ​​+ znaki w ciągu są zmienione na -. Naprawdę chciałbym móc przekazać ciąg znaków z dowolnymi znakami. – sfendell

+1

Widzę, o co pytasz ... Jeśli chcesz czytać w wielu ciągach bez cudzysłowów, użyj 'nargs = '+'', który mówi '-env', aby przeczytał jeden lub więcej ciągów. – SethMMorton

+0

Ale chciałbym, aby niektóre z tych stringów zawierały w nich kreski, nawet o tych samych nazwach, co argumenty w moim akapicie. Coś jak pythonowa kolejka aplikacji Application.py -env "-env blah" powinna działać. – sfendell

11

Ten problem został szczegółowo omówiony w artykule http://bugs.python.org/issue9334. Większość działań była w 2011 roku. Dodałem łatkę w zeszłym roku, ale jest sporo zaległości w łatkach argparse.

Kwestia jest potencjalną niejednoznacznością w ciągu znaków takim jak '--env' lub "-s WHATEVER -e COOL STUFF", gdy następuje po opcji, która przyjmuje argument.

optparse wykonuje proste przetwarzanie od lewej do prawej. Pierwszy --env jest flagą opcji, która przyjmuje jeden argument, więc zużywa następny, niezależnie od tego, jak wygląda. argparse, z drugiej strony, pętle przez ciągi dwa razy. Najpierw klasyfikuje je jako "O" lub "A" (flaga lub argument opcji). W drugiej pętli zużywa je, używając dopasowywania wzorców w celu uzyskania wartości zmiennych nargs. W tym przypadku wygląda na to, że mamy OO, dwie flagi i żadnych argumentów.

Rozwiązaniem przy użyciu argparse jest upewnienie się, że ciąg argumentów nie zostanie pomylony dla flagi opcji. Możliwości, które zostały pokazane tutaj (iw kwestii bug) obejmują:

--env="--env" # clearly defines the argument. 

--env " --env" # other non - character 
--env "--env " # space after 

--env "--env one two" # but not '--env "-env one two"' 

Sama '--env' wygląda flaga (nawet gdy cytowany zobaczyć sys.argv), ale kiedy następuje innych strunach nie. Ale "-env one two" ma problemy, ponieważ może być przetwarzany jako ['-e','nv one two'], flaga `` -e ', po której następuje ciąg (lub jeszcze więcej opcji).

-- i nargs=argparse.PARSER może być również użyty do wymuszenia na argparse wyświetlania wszystkich poniższych łańcuchów jako argumentów. Ale działają tylko na końcu listy argumentów.

Istnieje proponowana łatka w numerze9334, aby dodać tryb args_default_to_positional=True. W tym trybie parser klasyfikuje tylko łańcuchy jako flagi opcji, jeśli może jednoznacznie dopasować je do zdefiniowanych argumentów. Tak więc "--one" w "--env --one" zostanie zaklasyfikowane jako argument. Ale drugi "--env" w "--env --env" nadal będzie klasyfikowany jako flaga opcji.


Rozszerzając podobnego przypadku w

Using argparse with argument values that begin with a dash ("-")

parser = argparse.ArgumentParser(prog="PROG") 
parser.add_argument("-f", "--force", default=False, action="store_true") 
parser.add_argument("-e", "--extra") 
args = parser.parse_args() 
print(args) 

produkuje

1513:~/mypy$ python3 stack16174992.py --extra "--foo one" 
Namespace(extra='--foo one', force=False) 
1513:~/mypy$ python3 stack16174992.py --extra "-foo one" 
usage: PROG [-h] [-f] [-e EXTRA] 
PROG: error: argument -e/--extra: expected one argument 
1513:~/mypy$ python3 stack16174992.py --extra "-bar one" 
Namespace(extra='-bar one', force=False) 
1514:~/mypy$ python3 stack16174992.py -fe one 
Namespace(extra='one', force=True) 

W "-foo jeden" przypadek nie dlatego, że -foo jest interpretowane jako flagi -f plus nieokreślone dodatki. Jest to ta sama czynność, która umożliwia interpretowanie -fe jako ['-f','-e'].

Jeśli zmienię nargs do REMAINDER (nie PARSER), wszystko po -e jest interpretowany jako argumenty dla tej flagi:

parser.add_argument("-e", "--extra", nargs=argparse.REMAINDER) 

wszystkich przypadkach praca. Zauważ, że wartość jest listą. I cytaty nie są potrzebne:

1518:~/mypy$ python3 stack16174992.py --extra "--foo one" 
Namespace(extra=['--foo one'], force=False) 
1519:~/mypy$ python3 stack16174992.py --extra "-foo one" 
Namespace(extra=['-foo one'], force=False) 
1519:~/mypy$ python3 stack16174992.py --extra "-bar one" 
Namespace(extra=['-bar one'], force=False) 
1519:~/mypy$ python3 stack16174992.py -fe one 
Namespace(extra=['one'], force=True) 
1520:~/mypy$ python3 stack16174992.py --extra --foo one 
Namespace(extra=['--foo', 'one'], force=False) 
1521:~/mypy$ python3 stack16174992.py --extra -foo one 
Namespace(extra=['-foo', 'one'], force=False) 

argparse.REMAINDER jest jak „*”, oprócz tego, że bierze wszystko, co poniżej, czy to wygląda jak flaga czy nie. argparse.PARSER jest bardziej podobny do "+", ponieważ spodziewa się najpierw argumentu podobnego do positional. Użyty jest numer nargs.

używa od REMAINDER jest udokumentowana, https://docs.python.org/3/library/argparse.html#nargs

+2

Dziękuję bardzo: 'nargs = argparse.PARSER' pomógł mi. – guettli

+0

Nie jestem całkiem pewien, co się zmieniło, ale teraz 'python Application.py kolejka -env" -s WHATEVER -e COOL STUFF "' działa. 'queue -env" -foo "' nadal podnosi błąd, ponieważ autonomiczny '-foo' (lub "- foo") jest nadal interpretowany jako flaga. Przestrzenie po flagie aplikanta robią różnicę. – hpaulj

Powiązane problemy