multiple - python argparse-opcional adjuntar argumento con opciones
python flags example (5)
En la documentación (http://docs.python.org/dev/library/argparse.html#default), se dice:
Para los argumentos posicionales con nargs igual a? o *, el valor predeterminado se usa cuando no hay ningún argumento de línea de comando presente.
Entonces, si lo hacemos:
acts = [''clear'',''copy'',''dump'',''lock'']
p = argparse.ArgumentParser()
p.add_argument(''action'', nargs=''*'', choices=acts, default=''clear'')
print p.parse_args([])
Conseguimos lo que esperamos.
Namespace(action=''clear'')
El problema es cuando pones una lista por defecto. Pero lo he visto en el doc.
parser.add_argument(''bar'', nargs=''*'', default=[1, 2, 3], help=''BAR!'')
Por lo tanto, no sé :-(
De todos modos, aquí hay una solución que hace el trabajo que deseas:
import sys, argparse
acts = [''clear'',''copy'',''dump'',''lock'']
p = argparse.ArgumentParser()
p.add_argument(''action'', nargs=''*'', choices=acts)
args = [''dump'', ''clear''] # I set the default here ...
if sys.argv[1:]:
args = p.parse_args()
print args
Tengo un script donde le pido al usuario una lista de acciones predefinidas para realizar. También quiero la posibilidad de asumir una lista particular de acciones cuando el usuario no define nada. Sin embargo, parece que tratar de hacer ambos juntos es imposible.
cuando el usuario no da argumentos, recibe un error de que la opción predeterminada no es válida
acts = [''clear'',''copy'',''dump'',''lock'']
p = argparse.ArgumentParser()
p.add_argument(''action'', nargs=''*'', action=''append'', choices=acts, default=[[''dump'', ''clear'']])
args = p.parse_args([])
>>> usage: [-h] [{clear,copy,dump,lock} [{clear,copy,dump,lock} ...]]
: error: argument action: invalid choice: [[''dump'', ''clear'']] (choose from ''clear'', ''copy'', ''dump'', ''lock'')
y cuando definen un conjunto de acciones, el espacio de nombres resultante tiene las acciones del usuario anexadas al predeterminado, en lugar de reemplazar el predeterminado
acts = [''clear'',''copy'',''dump'',''lock'']
p = argparse.ArgumentParser()
p.add_argument(''action'', nargs=''*'', action=''append'', choices=acts, default=[[''dump'', ''clear'']])
args = p.parse_args([''lock''])
args
>>> Namespace(action=[[''dump'', ''clear''], [''dump'']])
La acción se estaba agregando debido al parámetro "action = ''append''" que pasaste a argparse.
Después de eliminar este parámetro, los argumentos pasados por un usuario se mostrarían por su cuenta, pero el programa generaría un error cuando no se pasaron los argumentos.
Agregar un prefijo ''-'' al primer parámetro resuelve esto de la manera más perezosa.
acts = [''clear'',''copy'',''dump'',''lock'']
p = argparse.ArgumentParser()
p.add_argument(''--action'', nargs=''*'', choices=acts, default=[[''dump'', ''clear'']])
args = p.parse_args()
La desventaja de este enfoque es que las opciones que pasa el usuario ahora deben ir precedidas por ''--action'', como:
app.py --action clear dump copy
Lo que necesita puede hacerse usando un argparse.Action
personalizado como en el siguiente ejemplo:
import argparse
parser = argparse.ArgumentParser()
class DefaultListAction(argparse.Action):
CHOICES = [''clear'',''copy'',''dump'',''lock'']
def __call__(self, parser, namespace, values, option_string=None):
if values:
for value in values:
if value not in self.CHOICES:
message = ("invalid choice: {0!r} (choose from {1})"
.format(value,
'', ''.join([repr(action)
for action in self.CHOICES])))
raise argparse.ArgumentError(self, message)
setattr(namespace, self.dest, values)
parser.add_argument(''actions'', nargs=''*'', action=DefaultListAction,
default = [''dump'', ''clear''],
metavar=''ACTION'')
print parser.parse_args([])
print parser.parse_args([''lock''])
La salida del script es:
$ python test.py
Namespace(actions=[''dump'', ''clear''])
Namespace(actions=[''lock''])
Puede probar si el usuario está suministrando acciones (en cuyo caso lo analiza como un argumento de posición requerido), o si no proporciona acciones (en cuyo caso lo analiza como un argumento opcional con valor predeterminado):
import argparse
import sys
acts = [''clear'', ''copy'', ''dump'', ''lock'']
p = argparse.ArgumentParser()
if sys.argv[1:]:
p.add_argument(''action'', nargs = ''*'', choices = acts)
else:
p.add_argument(''--action'', default = [''dump'', ''clear''])
args = p.parse_args()
print(args)
Cuando se ejecuta, produce estos resultados:
% test.py
Namespace(action=[''dump'', ''clear''])
% test.py lock
Namespace(action=[''lock''])
% test.py lock dump
Namespace(action=[''lock'', ''dump''])
Probablemente tengas otras opciones para analizar también. En ese caso, podría usar parse_known_args
para analizar las otras opciones, y luego manejar los argumentos unknown
en una segunda pasada:
import argparse
acts = [''clear'', ''copy'', ''dump'', ''lock'']
p = argparse.ArgumentParser()
p.add_argument(''--foo'')
args, unknown = p.parse_known_args()
if unknown:
p.add_argument(''action'', nargs = ''*'', choices = acts)
else:
p.add_argument(''--action'', default = [''dump'', ''clear''])
p.parse_args(unknown, namespace = args)
print(args)
Cuando se ejecuta, produce estos resultados:
% test.py
Namespace(action=[''dump'', ''clear''], foo=None)
% test.py --foo bar
Namespace(action=[''dump'', ''clear''], foo=''bar'')
% test.py lock dump
Namespace(action=[''lock'', ''dump''], foo=None)
% test.py lock dump --foo bar
Namespace(action=[''lock'', ''dump''], foo=''bar'')
Terminé haciendo lo siguiente:
- no
append
- agregue la lista vacía a las opciones posibles o de lo contrario los saltos de entrada vacíos
- sin defecto
- compruebe si hay una lista vacía después y establezca el valor predeterminado real en ese caso
Ejemplo:
parser = argparse.ArgumentParser()
parser.add_argument(
''is'',
type=int,
choices=[[], 1, 2, 3],
nargs=''*'',
)
args = parser.parse_args([''1'', ''3''])
assert args.a == [1, 3]
args = parser.parse_args([])
assert args.a == []
if args.a == []:
args.a = [1, 2]
args = parser.parse_args([''1'', ''4''])
# Error: ''4'' is not valid.