python3 - Argumentos Python Argparse condicionalmente requeridos
parser python example (5)
He investigado lo más posible, pero no he encontrado la mejor manera de hacer que ciertos argumentos de cmdline sean necesarios solo bajo ciertas condiciones, en este caso solo si se han dado otros argumentos. Esto es lo que quiero hacer en un nivel muy básico:
p = argparse.ArgumentParser(description=''...'')
p.add_argument(''--argument'', required=False)
p.add_argument(''-a'', required=False) # only required if --argument is given
p.add_argument(''-b'', required=False) # only required if --argument is given
Por lo que he visto, otras personas parecen agregar su propio cheque al final:
if args.argument and (args.a is None or args.b is None):
# raise argparse error here
¿Hay alguna manera de hacer esto de forma nativa dentro del paquete argparse?
Hasta que se solucione http://bugs.python.org/issue11588 , solo usaría nargs
:
p = argparse.ArgumentParser(description=''...'')
p.add_argument(''--arguments'', required=False, nargs=2, metavar=(''A'', ''B''))
De esta manera, si alguien proporciona --arguments
, tendrá 2 valores.
Quizás su resultado de CLI sea menos legible, pero el código es mucho más pequeño. Puedes arreglar eso con buenos documentos / ayuda.
He estado buscando una respuesta simple a este tipo de pregunta por algún tiempo. Todo lo que necesita hacer es verificar si ''--argument''
está en sys.argv
, así que básicamente para el ejemplo de código que podría hacer:
import argparse
import sys
if __name__ == ''__main__'':
p = argparse.ArgumentParser(description=''...'')
p.add_argument(''--argument'', required=False)
p.add_argument(''-a'', required=''--argument'' in sys.argv) #only required if --argument is given
p.add_argument(''-b'', required=''--argument'' in sys.argv) #only required if --argument is given
args = p.parse_args()
De esta manera, se required
sea True
o False
dependiendo de si el usuario utiliza o no el --argument
. Ya lo probé, parece funcionar y garantiza que -a
y -b
tienen un comportamiento independiente entre ellos.
Para los argumentos, he encontrado una solución rápida como esta. Supuestos: (1) ''--help'' debe mostrar ayuda y no quejarse del argumento requerido y (2) estamos analizando sys.argv
p = argparse.ArgumentParser(...)
p.add_argument(''-required'', ..., required = ''--help'' not in sys.argv )
Esto se puede modificar fácilmente para que coincida con una configuración específica. Para los posicionales requeridos (que no serán necesarios si, por ejemplo, se proporciona ''--help'' en la línea de comando), he encontrado lo siguiente: [los posicionales no permiten una palabra clave required=...
arg]
p.add_argument(''pattern'', ..., narg = ''+'' if ''--help'' not in sys.argv else ''*'' )
Básicamente, esto convierte el número de ocurrencias requeridas de ''patrón'' en la línea de comando de uno o más a cero o más en caso de que se especifique ''--help''.
Puede implementar una verificación proporcionando una acción personalizada para --argument
, que tomará un argumento de palabra clave adicional para especificar qué otras acciones deben ser necesarias si se usa --argument
.
import argparse
class CondAction(argparse.Action):
def __init__(self, option_strings, dest, nargs=None, **kwargs):
x = kwargs.pop(''to_be_required'', [])
super(CondAction, self).__init__(option_strings, dest, **kwargs)
self.make_required = x
def __call__(self, parser, namespace, values, option_string=None):
for x in self.make_required:
x.required = True
try:
return super(CondAction, self).__call__(parser, namespace, values, option_string)
except NotImplementedError:
pass
p = argparse.ArgumentParser()
x = p.add_argument("--a")
p.add_argument("--argument", action=CondAction, to_be_required=[x])
La definición exacta de CondAction
dependerá de lo que, exactamente, debe hacer --argument
. Pero, por ejemplo, si --argument
es un tipo de acción regular, toma un argumento y argparse._StoreAction
, entonces simplemente heredar de argparse._StoreAction
debería ser suficiente.
En el analizador de ejemplo, guardamos una referencia a la opción --a
dentro de la opción --argument
, y cuando se ve --argument
en la línea de comando, establece el indicador required
en --a
en True
. Una vez que se procesan todas las opciones, argparse
verifica que se haya establecido cualquier opción marcada como requerida.
Su prueba de análisis posterior está bien, especialmente si la prueba de valores predeterminados con is None
adecuada para sus necesidades.
http://bugs.python.org/issue11588 ''Add "necessarily inclusive" groups to argparse''
busca implementar este tipo de pruebas utilizando el mecanismo de groups
(una generalización de mutuall_exclusive_groups).
He escrito un conjunto de UsageGroups
de UsageGroups
que implementan pruebas como xor
(mutuamente excluyentes) and
, or
, y not
. Pensé que eran exhaustivos, pero no he podido expresar su caso en términos de esas operaciones. (parece que necesito nand
- no y, ver más abajo)
Esta secuencia de comandos utiliza una clase de Test
personalizada, que esencialmente implementa su prueba de análisis posterior. seen_actions
es una lista de acciones que el análisis ha visto.
class Test(argparse.UsageGroup):
def _add_test(self):
self.usage = ''(if --argument then -a and -b are required)''
def testfn(parser, seen_actions, *vargs, **kwargs):
"custom error"
actions = self._group_actions
if actions[0] in seen_actions:
if actions[1] not in seen_actions or actions[2] not in seen_actions:
msg = ''%s - 2nd and 3rd required with 1st''
self.raise_error(parser, msg)
return True
self.testfn = testfn
self.dest = ''Test''
p = argparse.ArgumentParser(formatter_class=argparse.UsageGroupHelpFormatter)
g1 = p.add_usage_group(kind=Test)
g1.add_argument(''--argument'')
g1.add_argument(''-a'')
g1.add_argument(''-b'')
print(p.parse_args())
La salida de muestra es:
1646:~/mypy/argdev/usage_groups$ python3 issue25626109.py --arg=1 -a1
usage: issue25626109.py [-h] [--argument ARGUMENT] [-a A] [-b B]
(if --argument then -a and -b are required)
issue25626109.py: error: group Test: argument, a, b - 2nd and 3rd required with 1st
usage
mensajes de usage
y error todavía necesitan trabajo. Y no hace nada que la prueba posterior al análisis no pueda.
Su prueba genera un error si (argument & (!a or !b))
. A la inversa, lo que está permitido es !(argument & (!a or !b)) = !(argument & !(a and b))
. Al agregar una prueba de UsageGroup
a mis clases de UsageGroup
, puedo implementar su caso como:
p = argparse.ArgumentParser(formatter_class=argparse.UsageGroupHelpFormatter)
g1 = p.add_usage_group(kind=''nand'', dest=''nand1'')
arg = g1.add_argument(''--arg'', metavar=''C'')
g11 = g1.add_usage_group(kind=''nand'', dest=''nand2'')
g11.add_argument(''-a'')
g11.add_argument(''-b'')
El uso es (usando !()
Para marcar una prueba ''nand''):
usage: issue25626109.py [-h] !(--arg C & !(-a A & -b B))
Creo que esta es la forma más breve y clara de expresar este problema utilizando grupos de uso de propósito general.
En mis pruebas, las entradas que se analizan con éxito son:
''''
''-a1''
''-a1 -b2''
''--arg=3 -a1 -b2''
Los que se supone que aumentan los errores son:
''--arg=3''
''--arg=3 -a1''
''--arg=3 -b2''