argparse subparser aide monolithique sortie
mon argparse n'a que 3 Drapeaux (store_true) au niveau supérieur, tout le reste est géré par des sous-traitants. Quand je run myprog.py --help
, la production affiche une liste de toutes les sous-commandes comme d'habitude, {sub1, sub2, sub3, sub4, ...}
. Ainsi, la valeur par défaut est génial de travailler...
en général, je ne me souviens pas du nom exact de la sous-commande dont j'ai besoin, ni de toutes ses options. Donc je finis par faire 2 recherches d'aide:
myprog.py --help
myprog.py sub1 --help
je le fais si souvent, que j'ai décidé de faire un pas en avant. Je préférerais avoir mon aide de haut niveau sortie d'un énorme Résumé, puis je fais défiler la liste manuellement. Je trouve que c'est beaucoup plus rapide (pour moi au moins).
j'utilisais une fonction RawDescriptionHelpFormatter, et j'ai tapé à la main la sortie longue help. Mais maintenant j'ai beaucoup de sous-commandes, et son devenir difficile à gérer.
Est-il possible d'obtenir une aide détaillée de sortie avec un seul appel de programme?
si ce n'est pas le cas, comment puis-je itérer les sous-analyseurs de mon instance argparse, puis récupérer la sortie d'aide individuellement de chacun (que je collerai ensuite ensemble)?
voici un aperçu de ma configuration argparse. J'ai nettoyé/dénudé le code un peu juste, donc cela ne peut pas fonctionner sans un peu d'aide.
parser = argparse.ArgumentParser(
prog='myprog.py',
formatter_class=argparse.RawDescriptionHelpFormatter,
description=textwrap.dedent(""" You can manually type Help here """) )
parser.add_argument('--debuglog', action='store_true', help='Verbose logging for debug purposes.')
parser.add_argument('--ipyonexit', action='store_true', help='Drop into an embeded Ipython session instead of exiting command.')
subparser = parser.add_subparsers()
### --- Subparser B
parser_b = subparser.add_parser('pdfreport', description="Used to output reports in PDF format.")
parser_b.add_argument('type', type=str, choices=['flatlist', 'nested', 'custom'],
help="The type of PDF report to generate.")
parser_b.add_argument('--of', type=str, default='',
help="Override the path/name of the output file.")
parser_b.add_argument('--pagesize', type=str, choices=['letter', '3x5', '5x7'], default='letter',
help="Override page size in output PDF.")
parser_b.set_defaults(func=cmd_pdf_report)
### ---- Subparser C
parser_c = subparser.add_parser('dbtables', description="Used to perform direct DB import/export using XLS files.")
parser_c.add_argument('action', type=str, choices=['push', 'pull', 'append', 'update'],
help="The action to perform on the Database Tables.")
parser_c.add_argument('tablename', nargs="+",
help="The name(s) of the DB-Table to operate on.")
parser_c.set_defaults(func=cmd_db_tables)
args = parser.parse_args()
args.func(args)
5 réponses
C'est un peu délicat, comme argparse ne pas exposer une liste de sous-analyseurs directement. Mais il peut être fait:
import argparse
# create the top-level parser
parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('--foo', action='store_true', help='foo help')
subparsers = parser.add_subparsers(help='sub-command help')
# create the parser for the "a" command
parser_a = subparsers.add_parser('a', help='a help')
parser_a.add_argument('bar', type=int, help='bar help')
# create the parser for the "b" command
parser_b = subparsers.add_parser('b', help='b help')
parser_b.add_argument('--baz', choices='XYZ', help='baz help')
# print main help
print(parser.format_help())
# retrieve subparsers from parser
subparsers_actions = [
action for action in parser._actions
if isinstance(action, argparse._SubParsersAction)]
# there will probably only be one subparser_action,
# but better save than sorry
for subparsers_action in subparsers_actions:
# get all subparsers and print help
for choice, subparser in subparsers_action.choices.items():
print("Subparser '{}'".format(choice))
print(subparser.format_help())
Cet exemple devrait fonctionner pour python 2.7 et python 3. L'exemple de l'analyseur de Python 2.7 documentation sur les sous-commandes argparse.
la seule chose à faire est d'ajouter un nouvel argument pour l'aide complète, ou de remplacer le construit dans -h/--help
.
Voici soulution complète avec aide personnalisée handler (presque tout le code de @Adaephon réponse):
import argparse
class _HelpAction(argparse._HelpAction):
def __call__(self, parser, namespace, values, option_string=None):
parser.print_help()
# retrieve subparsers from parser
subparsers_actions = [
action for action in parser._actions
if isinstance(action, argparse._SubParsersAction)]
# there will probably only be one subparser_action,
# but better save than sorry
for subparsers_action in subparsers_actions:
# get all subparsers and print help
for choice, subparser in subparsers_action.choices.items():
print("Subparser '{}'".format(choice))
print(subparser.format_help())
parser.exit()
# create the top-level parser
parser = argparse.ArgumentParser(prog='PROG', add_help=False) # here we turn off default help action
parser.add_argument('--help', action=_HelpAction, help='help for help if you need some help') # add custom help
parser.add_argument('--foo', action='store_true', help='foo help')
subparsers = parser.add_subparsers(help='sub-command help')
# create the parser for the "a" command
parser_a = subparsers.add_parser('a', help='a help')
parser_a.add_argument('bar', type=int, help='bar help')
# create the parser for the "b" command
parser_b = subparsers.add_parser('b', help='b help')
parser_b.add_argument('--baz', choices='XYZ', help='baz help')
parsed_args = parser.parse_args()
une façon plus simple d'itérer sur les sous-répertoires dans L'exemple D'Adaephon est
for subparser in [parser_a, parser_b]:
subparser.format_help()
Python vous permet d'accéder à des attributs cachés comme parser._actions
, mais ce n'est pas encouragée. Il est tout aussi facile de construire votre propre liste tout en définissant l'analyseur. En va de même pour faire des choses spéciales avec les arguments. add_argument
et add_subparser
retourner leurAction
et Parser
objets pour une raison.
si je faisais une sous-classe de ArgumentParser
je me sentirais libre à utilisez _actions
. Mais pour une application unique, construire ma propre liste serait plus clair.
Un exemple:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('mainpos')
parser.add_argument('--mainopt')
sp = parser.add_subparsers()
splist = [] # list to collect subparsers
sp1 = sp.add_parser('cmd1')
splist.append(sp1)
sp1.add_argument('--sp1opt')
sp2 = sp.add_parser('cmd2')
splist.append(sp2)
sp2.add_argument('--sp2opt')
# collect and display for helps
helps = []
helps.append(parser.format_help())
for p in splist:
helps.append(p.format_help())
print('\n'.join(helps))
# or to show just the usage
helps = []
helps.append(parser.format_usage())
for p in splist:
helps.append(p.format_usage())
print(''.join(helps))
Le combiné à l'utilisation de l'affichage est le suivant:
usage: stack32607706.py [-h] [--mainopt MAINOPT] mainpos {cmd1,cmd2} ...
usage: stack32607706.py mainpos cmd1 [-h] [--sp1opt SP1OPT]
usage: stack32607706.py mainpos cmd2 [-h] [--sp2opt SP2OPT]
l'affichage des aides combinées est long et redondant. Il peut être édité de différentes manières, soit après le formatage, soit avec des formateuses spéciales d'aide. Mais qui va faire de tels choix?
j'ai aussi pu imprimer une courte aide pour les commandes utilisant _choices_actions
.
def print_help(parser):
print(parser.description)
print('\ncommands:\n')
# retrieve subparsers from parser
subparsers_actions = [
action for action in parser._actions
if isinstance(action, argparse._SubParsersAction)]
# there will probably only be one subparser_action,
# but better save than sorry
for subparsers_action in subparsers_actions:
# get all subparsers and print help
for choice in subparsers_action._choices_actions:
print(' {:<19} {}'.format(choice.dest, choice.help))
j'ai quelques wrappers simples qui stockent de diverses références (Analyseur, sous-Analyseur, StoreAction) séquentiellement, pour une itération facile pendant la génération d'aide.
je suis maintenant organisé, verbeux, sortie d'aide générée automatiquement. Je vais poster un aperçu quand j'ai une chance.
il y a un inconvénient, et qui a à voir avec le contenu d'aide généré sur les Arguments optionnels: il n'est pas très bon. L'amélioration de ces sorties d'aide nécessitera plus qu'une enveloppe (si nous tu veux le garder propre). Mais si vous voulez une bonne vue d'ensemble de l'aide pour les programmes en évolution, cela devrait satisfaire la plupart.