Siento que hay una pequeña limitación con los sub_parsers en argparse, por ejemplo, tiene un conjunto de herramientas que pueden tener opciones similares que pueden extenderse a diferentes niveles. Puede ser raro tener esta situación, pero si está escribiendo código enchufable/modular, podría suceder.
Tengo el siguiente ejemplo. Es inverosímil y no está bien explicado en este momento, ya que es bastante tarde, pero aquí va:
Usage: tool [-y] {a, b}
a [-x] {create, delete}
create [-x]
delete [-y]
b [-y] {push, pull}
push [-x]
pull [-x]
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('-x', action = 'store_true')
parser.add_argument('-y', action = 'store_true')
subparsers = parser.add_subparsers(dest = 'command')
parser_a = subparsers.add_parser('a')
parser_a.add_argument('-x', action = 'store_true')
subparsers_a = parser_a.add_subparsers(dest = 'sub_command')
parser_a_create = subparsers_a.add_parser('create')
parser_a_create.add_argument('-x', action = 'store_true')
parser_a_delete = subparsers_a.add_parser('delete')
parser_a_delete.add_argument('-y', action = 'store_true')
parser_b = subparsers.add_parser('b')
parser_b.add_argument('-y', action = 'store_true')
subparsers_b = parser_b.add_subparsers(dest = 'sub_command')
parser_b_create = subparsers_b.add_parser('push')
parser_b_create.add_argument('-x', action = 'store_true')
parser_b_delete = subparsers_b.add_parser('pull')
parser_b_delete.add_argument('-y', action = 'store_true')
print parser.parse_args(['-x', 'a', 'create'])
print parser.parse_args(['a', 'create', '-x'])
print parser.parse_args(['b', '-y', 'pull', '-y'])
print parser.parse_args(['-x', 'b', '-y', 'push', '-x'])
salida
Namespace(command='a', sub_command='create', x=True, y=False)
Namespace(command='a', sub_command='create', x=True, y=False)
Namespace(command='b', sub_command='pull', x=False, y=True)
Namespace(command='b', sub_command='push', x=True, y=True)
Como se puede ver, es difícil distinguir dónde a lo largo de la cadena se estableció cada argumento. Puede resolver esto cambiando el nombre de cada variable. Por ejemplo, puedes establecer 'dest' en 'x', 'a_x', 'a_create_x', 'b_push_x', etc., pero eso sería doloroso y difícil de separar.
Una alternativa sería detener el ArgumentParser una vez que llega a un subcomando y pasar los argumentos restantes a otro analizador independiente para que pueda generar objetos separados. Puede intentar lograrlo utilizando 'parse_known_args()' y no definiendo argumentos para cada subcomando. Sin embargo, eso no sería bueno porque cualquier argumento no analizado de antes todavía estaría allí y podría confundir el programa.
Me parece un poco barato, pero una solución útil es tener argparse interpretar los siguientes argumentos como cadenas en una lista. Esto se puede hacer estableciendo el prefijo en un terminador nulo '\ 0' (o algún otro carácter 'difícil de usar') - si el prefijo está vacío, el código emitirá un error, al menos en Python 2.7. 3.
Ejemplo:
parser = ArgumentParser()
parser.add_argument('-x', action = 'store_true')
parser.add_argument('-y', action = 'store_true')
subparsers = parser.add_subparsers(dest = 'command')
parser_a = subparsers.add_parser('a' prefix_chars = '\0')
parser_a.add_argument('args', type = str, nargs = '*')
print parser.parse_args(['-xy', 'a', '-y', '12'])
Salida:
Namespace(args=['-y', '12'], command='a', x=True, y=True)
Tenga en cuenta que no consume la segunda opción -y
. A continuación, puede pasar el resultado 'args' a otro ArgumentParser.
inconvenientes:
- Ayuda no podría manejarse bien. Tendría que hacer algo más de solución con esto
- Encontrar errores podría ser difícil de rastrear y requerir un esfuerzo adicional para asegurarse de que los mensajes de error estén correctamente encadenados.
- Un poco más de sobrecarga asociada con los múltiples ArgumentParsers.
Si alguien tiene más información al respecto, házmelo saber.
La empresa en la que estoy trabajando para una línea de base tiene v2.6 por lo que usar argparse es un problema, ya que tendría que ser incluido como una biblioteca externa y sólo si es necesario cargado. Lejos de ser imposible, simplemente no es ideal. En cuanto a la biblioteca cmdln, me da un poco de funcionalidad básica que preferiría no recrear. Dicho esto, me opongo a usar otra cosa. – tima