2016-04-07 10 views
0

Ich versuche, einen Benutzer zu ermöglichen, einen Funktionsnamen zu übergeben. Aus irgendeinem Grund scheint argparse die Typüberprüfung/-konvertierung durchzuführen, BEVOR sie die Auswahlmöglichkeiten prüft. Ist das ein Fehler? Beste Sache zu tun?Python argparse check Auswahl vor Typ

import argparse 

def foo(): 
    return 'foo' 

def bar(): 
    return 'bar' 

parser = argparse.ArgumentParser() 
functions = {f.__name__:f for f in [foo, bar]} 
parser.add_argument("function", type=lambda f: functions.get(f), help="which function", choices=functions) 
args = parser.parse_args() 
print(args.function()) 

Dies wirft:

$ python blah.py foo 
usage: blah.py [-h] {foo,bar} 
blah.py: error: argument function: invalid choice: <function foo at 0x7f65746dd848> (choose from 'foo', 'bar') 
+2

Von [ 'argparse'] (https://docs.python.org/3/library/argparse.html#choices) Dokumentation: ' Beachten Sie, dass die Aufnahme in den Behälter Auswahl wird nach jeder Art geprüft Conversions wurde durchgeführt, so dass der Typ der Objekte im Auswahlcontainer dem angegebenen Typ entsprechen sollte: ' – wnnmaw

Antwort

2

Ja, während der Analyse der type dann choices Reihenfolge ist klar und absichtlich (und nicht nur zufällig). Bei der Vorbereitung arg_strings zum namespace zuweisen nennt es _get_values, das tut:

def _get_values(self, action, arg_strings) 
     .... (various nargs tests) 
     value = self._get_value(action, arg_string) 
     self._check_value(action, value) 
     return value 

wo _get_value die action.type Funktion gilt, und _check_value Tests

value not in action.choices 

Für choices hat Parsen nur auf die in zu reagieren (__contains__) Ausdruck.

So müssen choices Werte nach der Konvertierung widerspiegeln. Wenn typeint ist, dann ist choices=[1,2,3] korrekt, ['1','2','3'] ist nicht.

Es gibt einige (weitgehend unaufgelöste) Bugprobleme bei der Anzeige der Auswahlmöglichkeiten. Lange Listen, z.B. range(100) arbeiten in Parsing, aber nicht gut angezeigt. Und die Anzeige erfordert auch, dass choices iterierbar ist (z. B. eine Liste, ein Tupel, ein Wörterbuch). Dieses Anzeigeproblem betrifft die Verwendung, die Hilfe und die Fehlermeldungen (jedes Format choices etwas anders).

metavar ist Ihr leistungsstärkstes Werkzeug zum Ersetzen einer unerwünschten choices Liste. Ich müsste einen Testfall ausführen, um zu sehen, ob er die Dinge für alle 3 Situationen löst.

1

Offenbar so, aber Sie können dies umgehen, indem einfach functions.keys() als Ihre Wahlen, z.B.

import argparse 

def foo(): 
    return 'foo' 

def bar(): 
    return 'bar' 

parser = argparse.ArgumentParser() 
functions = {f.__name__:f for f in [foo, bar]} 
parser.add_argument("function", type=lambda f: functions.get(f), help="which function", choices=functions.values()) 
args = parser.parse_args() 
print(args.function()) 

Wenn Sie jedoch diese Art der Schnittstelle zur Verfügung stellen möchten (Abbildungsfunktionen Zeilenargumente gebieten) Sie vielleicht einen Blick auf click nehmen wollen.

+0

Ja, es gibt mehrere offensichtliche Problemumgehungen. Ich kann auch einfach type = str und dann Funktionen [args.function]() aufrufen. Meistens war ich mürrisch, dass --help die Wahlmöglichkeiten vor dem "Tippen" anzeigt, aber der Parser prüft die Auswahl nach dem "Tippen". Hatte gehofft, dass es einen generischen Weg gab, mit dieser Situation umzugehen. – Scott

+0

Ha, verstanden. Für das, was es wert ist, würde ich empfehlen, die 'str'-Namen für Befehle zu verwenden (' type = str, options = functions.keys() '), um die Fehlermeldung bei der Eingabe des falschen Werts zu geben ) ' – mfitzp

+1

Offenbar können Sie die" Auswahlmöglichkeiten "in der Hilfe unterdrücken, indem Sie" metavar = 'FUNCTION' "angeben und die Auswahlmöglichkeiten mit einfachen Stringmanipulationen wieder so in die Hilfe einfügen, wie Sie möchten. Das werde ich machen, denke ich ... zusammen mit deinem .values ​​() Vorschlag. Danke. – Scott