web-dev-qa-db-fra.com

sortie d'aide monolithique argparse subparser

Mon argparse n'a que 3 drapeaux (store_true) au niveau supérieur, tout le reste est géré par des sous-vendeurs. Lorsque je lance myprog.py --help, la sortie affiche une liste de toutes les sous-commandes comme d'habitude, {sub1, sub2, sub3, sub4, ...}. Donc, le défaut fonctionne très bien ...

Je ne me souviens généralement pas du nom exact de la sous-commande dont j'ai besoin et de toutes ses options. Alors 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 le regrouper en une seule étape. Je préférerais que mon aide de niveau supérieur produise un résumé énorme, puis je fais défiler la liste manuellement. Je trouve que c'est beaucoup plus rapide (pour moi du moins).

J'utilisais RawDescriptionHelpFormatter et saisissais manuellement l'aide longue. Mais maintenant, j'ai beaucoup de sous-commandes et cela devient difficile à gérer.

Existe-t-il un moyen d'obtenir une sortie d'aide détaillée avec un seul appel de programme? 

Si ce n'est pas le cas, comment puis-je parcourir les sous-vendeurs de mon instance argparse, puis récupérer l'aide de chaque fichier (que je vais ensuite coller ensemble)?


Voici un aperçu rapide de ma configuration argparse. J'ai nettoyé/effacé le code un peu, 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)
25
user2097818

C'est un peu délicat, car argparse n'expose pas directement une liste de sous-analyseurs définis. Mais cela 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 avec python 2.7 et python 3. L'exemple d'analyse est tiré de la documentation Python 2.7 sur les sous-commandes argparse .

Il ne reste plus qu'à ajouter un nouvel argument pour l'aide complète ou à remplacer le -h/--help intégré.

13
Adaephon

Voici la solution complète avec le gestionnaire d’aide personnalisé (presque tout le code de @Adaephon answer):

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
grundic

Un moyen plus simple d'itérer sur les sous-vendeurs 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 tels que parser._actions, mais cela n'est pas recommandé. Il est tout aussi facile de créer votre propre liste tout en définissant l’analyseur. Même chose pour faire des choses spéciales avec les arguments. add_argument et add_subparser retournent leurs objets Action et Parser respectifs pour une raison.

Si je faisais une sous-classe de ArgumentParser, je me sentirais libre d’utiliser _actions. Mais pour une application unique, la construction de ma propre liste serait plus claire.


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))

L’affichage «utilisation» combiné est:

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 formateurs d'aide spéciaux. Mais qui va faire de tels choix?

3
hpaulj

J'ai également été en mesure d'imprimer une aide courte 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
anatoly techtonik