La risposta breve è che parse_args
utilizza parse_known_args
. Questo metodo consente di gestire argomenti sconosciuti come --gold
. Di conseguenza, gli errori di tipo argomento vengono generati prima degli errori unknown arguments
.
Ho aggiunto una soluzione che include la sottoclasse ArgumentParser
e la modifica di un metodo nel suo stack chiamante.
Cercherò di delineare parse_args
applicata al tuo esempio.
La prima cosa che fa è categorizzare le stringhe come O
o A
. In parole semplici, quelli che iniziano con -
sono O
, altri A
. Prova anche a far corrispondere i O
con un argomento definito.
Nel tuo esempio, trova OAA
. Regex è usato per abbinare questa stringa ai pattern definiti dall'argomento nargs
. (se necessario, posso spiegare più dettagliatamente questo passaggio)
--gold
non corrisponde; ad un certo punto (in questo ciclo iniziale o successivo) viene inserito in un elenco extras
. (Controllerò il codice per i dettagli).
Per il 2 ° ciclo attraverso le stringhe, esso tenta alternativamente di posizionare postionals e optionals.
È quando si cerca di far corrispondere lo 5
con starttime
che la classe Action solleva l'errore di tipo, che si propaga fino a stamparne l'utilizzo e l'uscita. Poiché --gold
non è definito, 5
non viene utilizzato come argomento facoltativo.Quindi viene analizzato come la prima stringa posizionale. (Alcuni tipi di optionals accettano 0 argomenti, quindi non assume nulla che segua uno --...
è un argomento opzionale).
Penso che senza lo 5
, l'ultima stringa corrispondesse. parse_known_args
restituirebbe con --gold
nel termine extras
. parse_args
utilizza parse_known_args
ma genera un errore quando extras
non è vuoto.
Quindi in un certo senso il parser rileva entrambi gli errori, ma è lo starttime
quello che attiva il messaggio di errore. Aspetta fino alla fine per lamentarsi di non riconosciuto --gold
.
Come filosofia generale, argparse
non tenta di rilevare e presentare tutti gli errori. Non raccoglie un elenco di errori da presentare in un messaggio completo finale.
Esaminerò il codice per verificare i dettagli. Non penso che tu possa facilmente cambiare il modello di analisi di base. Se penso a un modo per forzare un precedente errore unrecognized option
, modifico questa risposta.
def _parse_optional(self, arg_string):
cerca di classificare in una stringa argv
. Se la stringa è simile a positional
restituisce None
. Se corrisponde a una stringa_opzione Action, restituisce una tupla '(action, option_string, None) `con l'azione corrispondente. Infine, se non corrispondono, restituisce:
# it was meant to be an optional but there is no such option
# in this parser (though it might be a valid option in a subparser)
return None, arg_string, None
Credo che questo sia ciò che accade con il vostro --gold
. Nota il motivo per cui potrebbe essere ancora un'opzione valida.
Questa funzione viene richiamata da
def _parse_known_args(self, arg_strings, namespace):
...
for i, arg_string in enumerate(arg_strings_iter):
....
option_tuple = self._parse_optional(arg_string)
if option_tuple is None:
pattern = 'A'
else:
option_string_indices[i] = option_tuple
pattern = 'O'
arg_string_pattern_parts.append(pattern)
...
# at the end
# return the updated namespace and the extra arguments
return namespace, extras
raccolta che 'AOO'
modello, nonché un elenco di tali tuple.
Durante il 2 ° ciclo si alterna tra posizioni di consumo e opzioni. La funzione che consuma un optional è:
def consume_optional(start_index):
option_tuple = option_string_indices[start_index]
action, option_string, explicit_arg = option_tuple
if action is None:
extras.append(arg_strings[start_index])
...otherwise...
take_action(action, args, option_string)
Come ho scritto in precedenza, il vostro --gold
viene messo sulla lista extras
, mentre 5
rimane sulla lista di argomenti che possono essere analizzati come positionals.
Il namespace
e extras
sono passati attraverso parse_known_args
a voi, l'utente, o per parse_args
.
È plausibile che sia possibile creare una sottoclasse di ArgumentParser
e definire un metodo _parse_optional
modificato. Potrebbe generare un errore invece di restituire la tupla (None, arg_string, None)
.
import argparse
import datetime
class MyParser(argparse.ArgumentParser):
def _parse_optional(self, arg_string):
arg_tuple = super(MyParser, self)._parse_optional(arg_string)
if arg_tuple is None:
return arg_tuple # positional
else:
if arg_tuple[0] is not None:
return arg_tuple # valid optional
else:
msg = 'error: no such option: %s'%arg_string
self.error(msg)
def convertIsoTime(timestamp):
"""read ISO-8601 time-stamp using the AMS conventional format YYYY-MM-DDThh:mm:ssUTC"""
try:
return datetime.datetime.strptime(timestamp,"%Y-%m-%dT%H:%M:%SUTC")
except:
raise argparse.ArgumentTypeError("'{}' is not a valid ISO-8601 time-stamp".format(timestamp))
# parser = argparse.ArgumentParser()
parser = MyParser()
parser.add_argument('startTime', type=convertIsoTime)
parser.add_argument('--good', type=int,
help='foo')
args = parser.parse_args(['--good','5','2015-01-01T00:00:00UTC'])
print(args)
args = parser.parse_args(['--gold','5','2015-01-01T00:00:00UTC'])
produce
1505:~/mypy$ python3 stack31317166.py
Namespace(good=5, startTime=datetime.datetime(2015, 1, 1, 0, 0))
usage: stack31317166.py [-h] [--good GOOD] startTime
stack31317166.py: error: error: no such option: --gold
Subclassing per fornire un'azione personalizzata è buono argparse
(e Python) la pratica.
Se si desidera maggiore considerazione di questo caso da parte degli sviluppatori Python, prendere in considerazione la scrittura di un bug/issue
(a PEP è per idee formali più sviluppate). Ma c'è abbastanza arretrato di errori/patch argparse
e molta prudenza sulla retrocompatibilità.
http://bugs.python.org/issue?%40columns=id%2Cactivity%2Ctitle%2Ccreator%2Cassignee%2Cstatus%2Ctype&%40sort=-activity&%40filter=status&%40action=searchid&ignore=file%3Acontent&%40search_text=_parse_optional&submit=search&status=-1%2C1%2C2%2C3
è un elenco di bug/problemi che fanno riferimento _parse_optional
. Le possibili modifiche includono come vengono gestiti gli optionals ambigui. (Le scansionerò per vedere se dimentico qualcosa. Alcune patch sono mie.) Ma usando super
, la mia modifica suggerita non è influenzata dalle modifiche all'interno della funzione. È influenzato solo dai cambiamenti nel modo in cui viene chiamata la funzione e da ciò che viene restituito, che è molto meno probabile che si verifichi. Presentando il tuo problema, devi almeno avvisare gli sviluppatori che qualcuno dipende da questa interfaccia.
Grazie per una spiegazione dettagliata e utile. La mia unica preoccupazione sarebbe che potrebbe rompersi se Argparse viene aggiornato. Aggiungerò una richiesta al backlog argparse. –
Ho aggiunto una nota su problemi esistenti. – hpaulj