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)
24
demandé sur user2097818 2013-11-20 15:06:04

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.

13
répondu Adaephon 2013-11-20 12:35:13

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()
11
répondu grundic 2014-06-09 14:46:46

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?

3
répondu hpaulj 2015-09-16 16:33:52

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))
0
répondu anatoly techtonik 2017-03-21 23:46:30

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.

-2
répondu user2097818 2013-11-26 23:31:40