python - pyimagesearch - Argparse-Cómo especificar un subcomando predeterminado
arguments parser python (2)
Estoy utilizando el paquete argparse de Python 2.7 para escribir alguna lógica de análisis de opciones para una herramienta de línea de comandos. La herramienta debe aceptar uno de los siguientes argumentos:
"ON": enciende una función.
"OFF": desactiva una función.
[No se proporcionan argumentos]: se hace eco del estado actual de la función.
Mirar la documentación argparse me llevó a creer que quería que se definieran dos subcomandos, posiblemente tres, ya que estos tres estados se excluyen mutuamente y representan diferentes actividades conceptuales. Este es mi intento actual en el código:
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser.set_defaults(func=print_state) # I think this line is wrong.
parser_on = subparsers.add_parser(''ON'')
parser_on.set_defaults(func=set_state, newstate=''ON'')
parser_off = subparsers.add_parser(''OFF'')
parser_off.set_defaults(func=set_state, newstate=''OFF'')
args = parser.parse_args()
if(args.func == set_state):
set_state(args.newstate)
elif(args.func == print_state):
print_state()
else:
args.func() # Catchall in case I add more functions later
Tenía la impresión de que si proporcionaba 0 argumentos, el analizador principal configuraría func=print_state
, y si proporcionaba 1 argumento, el analizador principal usaría los valores predeterminados del subcomando apropiado y llamaría func=set_state
. En su lugar, me sale el siguiente error con 0 argumentos:
usage: cvsSecure.py [-h] {ON,OFF} ...
cvsSecure.py: error: too few arguments
Y si proporciono "OFF" u "ON", se llama a set_state
lugar de set_state
. Si comento fuera de la línea set_state
, set_state
se llama correctamente.
Soy un programador a nivel de oficial, pero un principiante de rango para Python. ¿Alguna sugerencia sobre cómo puedo hacer que esto funcione?
Edit : Otra razón por la que estaba viendo subcomandos fue una cuarta función potencial que estoy considerando para el futuro:
"FORCE txtval": establece el estado de la función en txtval
.
Hay dos problemas con su enfoque.
En primer lugar, probablemente ya haya notado que newstate
no es un subvalor del sub parser y debe abordarse en el nivel superior de args.newstate
como args.newstate
. Eso debería explicar que al asignar un valor predeterminado a newstate
dos veces, se sobrescribirá el primer valor. Ya sea que llame a su programa con ''ON'' o ''OFF'' como parámetro, cada vez que se set_state()
con OFF
. Si solo quieres poder hacer python cvsSecure ON
y python cvsSecure OFF
lo siguiente funcionaría:
from __future__ import print_function
import sys
import argparse
def set_state(state):
print("set_state", state)
def do_on(args):
set_state(''ON'')
def do_off(args):
set_state(''OFF'')
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser_on = subparsers.add_parser(''ON'')
parser_on.set_defaults(func=do_on)
parser_on.add_argument(''--fast'', action=''store_true'')
parser_off = subparsers.add_parser(''OFF'')
parser_off.set_defaults(func=do_off)
args = parser.parse_args()
args.func(args)
El segundo problema es que argparse
maneja subparsers como argumentos de valor único, por lo que debe especificar uno antes de invocar parser.parse_args()
. Puede automatizar la inserción de un argumento faltante agregando un subparser adicional ''PRINT'' e insertándolo automáticamente usando set_default_subparser
agregado a argparse.ArgumentParser()
(ese código es parte del paquete ruamel.std.argparse
from __future__ import print_function
import sys
import argparse
def set_default_subparser(self, name, args=None):
"""default subparser selection. Call after setup, just before parse_args()
name: is the name of the subparser to call by default
args: if set is the argument list handed to parse_args()
, tested with 2.7, 3.2, 3.3, 3.4
it works with 2.6 assuming argparse is installed
"""
subparser_found = False
for arg in sys.argv[1:]:
if arg in [''-h'', ''--help'']: # global help if no subparser
break
else:
for x in self._subparsers._actions:
if not isinstance(x, argparse._SubParsersAction):
continue
for sp_name in x._name_parser_map.keys():
if sp_name in sys.argv[1:]:
subparser_found = True
if not subparser_found:
# insert default in first position, this implies no
# global options without a sub_parsers specified
if args is None:
sys.argv.insert(1, name)
else:
args.insert(0, name)
argparse.ArgumentParser.set_default_subparser = set_default_subparser
def print_state(args):
print("print_state")
def set_state(state):
print("set_state", state)
def do_on(args):
set_state(''ON'')
def do_off(args):
set_state(''OFF'')
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser_print = subparsers.add_parser(''PRINT'', help=''default action'')
parser_print.set_defaults(func=print_state)
parser_on = subparsers.add_parser(''ON'')
parser_on.set_defaults(func=do_on)
parser_on.add_argument(''--fast'', action=''store_true'')
parser_off = subparsers.add_parser(''OFF'')
parser_off.set_defaults(func=do_off)
parser.set_default_subparser(''PRINT'')
args = parser.parse_args()
args.func(args)
No necesita manejar en args
a do_on()
, etc., pero es útil si comienza a especificar opciones para los diferentes subparsers.
Los valores predeterminados del analizador de nivel superior anulan los valores predeterminados en los subparadores, por lo que se ignora el establecimiento del valor predeterminado de func
en los subparadores, pero el valor de newstate
de los valores predeterminados del subparador es correcto.
No creo que quieras usar subcomandos. Los subcomandos se utilizan cuando las opciones disponibles y los argumentos posicionales cambian según el subcomando que se elija. Sin embargo, no tiene otras opciones o argumentos posicionales.
El siguiente código parece hacer lo que necesita:
import argparse
def print_state():
print "Print state"
def set_state(s):
print "Setting state to " + s
parser = argparse.ArgumentParser()
parser.add_argument(''state'', choices = [''ON'', ''OFF''], nargs=''?'')
args = parser.parse_args()
if args.state is None:
print_state()
elif args.state in (''ON'', ''OFF''):
set_state(args.state)
Tenga en cuenta los parámetros opcionales para parser.add_argument
. El parámetro "opciones" especifica las opciones permitidas, mientras se configura "nargs" en "?" especifica que se debe consumir 1 argumento si está disponible, de lo contrario no se debe consumir ninguno.
Editar: Si desea agregar un comando FORCE con un argumento y tener un texto de ayuda separado para el comando ON y OFF, entonces necesita usar subcomandos. Desafortunadamente, no parece haber una forma de especificar un subcomando predeterminado. Sin embargo, puede solucionar el problema buscando una lista de argumentos vacía y proporcionando la suya propia. Aquí hay un código de ejemplo que ilustra lo que quiero decir:
import argparse
import sys
def print_state(ignored):
print "Print state"
def set_state(s):
print "Setting state to " + s
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
on = subparsers.add_parser(''ON'', help = ''On help here.'')
on.set_defaults(func = set_state, newstate = ''ON'')
off = subparsers.add_parser(''OFF'', help = ''Off help here.'')
off.set_defaults(func = set_state, newstate = ''OFF'')
prt = subparsers.add_parser(''PRINT'')
prt.set_defaults(func = print_state, newstate = ''N/A'')
force = subparsers.add_parser(''FORCE'' , help = ''Force help here.'')
force.add_argument(''newstate'', choices = [ ''ON'', ''OFF'' ])
force.set_defaults(func = set_state)
if (len(sys.argv) < 2):
args = parser.parse_args([''PRINT''])
else:
args = parser.parse_args(sys.argv[1:])
args.func(args.newstate)