2013-11-20 7 views
21

Il mio argparse ha solo 3 flag (store_true) al livello superiore, tutto il resto viene gestito tramite i subparatori. Quando eseguo myprog.py --help, l'output mostra un elenco di tutti i comandi secondari, come normale, {sub1, sub2, sub3, sub4, ...}. Quindi, l'impostazione predefinita funziona perfettamente ...output di aiuto monolitico di subparser di argparse

Di solito non riesco a ricordare il nome esatto del sottocomando che mi serve e tutte le sue opzioni. Così finisco per fare 2 aiuto ricerche:

myprog.py --help 
myprog.py sub1 --help 

Lo faccio così spesso, ho deciso di stipare questo in un solo passo. Preferirei che il mio aiuto di primo livello fornisse un sommario enorme, quindi scorrerò l'elenco manualmente. Trovo che sia molto più veloce (almeno per me).

Stavo usando un RawDescriptionHelpFormatter e sto scrivendo a mano l'output di aiuto lungo. Ma ora ho molti sottocomandi e sta diventando un problema da gestire.

C'è un modo per ottenere un output dettagliato con una sola chiamata di programma?

In caso contrario, come posso eseguire l'iterazione dei subparser della mia istanza argparse e quindi recuperare l'output della guida singolarmente da ognuno (che poi incollerò insieme)?


Ecco una breve descrizione del mio setup argparse. Ho pulito/spogliato il codice un bel po ', quindi questo potrebbe non funzionare senza un po' di aiuto.

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

Mostraci un piccolo esempio con un po 'di codice, solo un paio di opzioni e un paio di subparser. –

risposta

9

Questo è un po 'complicato, poiché argparse non espone direttamente un elenco di sub-parser definiti. Ma si può fare:

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

Questo esempio dovrebbe funzionare per Python 2.7 e Python 3. L'esempio parser è da Python 2.7 documentation on argparse sub-commands.

L'unica cosa che resta da fare è aggiungere un nuovo argomento per l'aiuto completo, o sostituire il built-in -h/--help.

+0

Ottimo esempio. Questo sta generando un buon risultato per me. Non sono sicuro di come ridefinire l'argomento -h/- help nel mio caso, poiché gli argomenti facoltativi non mi piace seguire i miei subparser. Anche se, potrei semplicemente definire un altro subparser, chiamato 'help', come ultimo, e può ispezionare tutto ciò che è stato aggiunto prima di esso. – user2097818

2

Un modo più semplice per scorrere i subparsers nell'esempio di Adaephon è

for subparser in [parser_a, parser_b]: 
    subparser.format_help() 

Python fa consentono di accedere attributi nascosti come parser._actions, ma che non è incoraggiato. È altrettanto facile costruire la tua lista mentre definisci il parser. Lo stesso vale per fare cose speciali con gli argomenti. add_argument e add_subparser restituiscono i rispettivi oggetti Action e Parser per un motivo.

Se si stesse creando una sottoclasse di ArgumentParser, mi sento libero di utilizzare _actions. Ma per un'applicazione unica, costruire la mia lista sarebbe più chiaro.


Un esempio:

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

Il display combinato 'utilizzo' è:

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] 

Il display aiuta il combinato è lungo e ridondante.Potrebbe essere modificato in vari modi, sia dopo la formattazione, sia con i formattatori di aiuto speciali. Ma chi sta facendo queste scelte?

-1

Ho alcuni semplici wrapper che memorizzano i vari riferimenti (Parser, SubParser, StoreAction) in sequenza, per una facile iterazione durante la generazione di aiuto.

Ora mi sto organizzando, dettagliato, generato automaticamente dall'output della guida. Pubblicherò una panoramica quando avrò la possibilità.

C'è uno svantaggio, e che ha a che fare con il contenuto della guida generato su Argomenti opzionali: non è molto buono. Migliorare questi output di aiuto richiederà più di un wrapper (se vogliamo tenerlo pulito). Ma se vuoi una buona panoramica di aiuto per i programmi in evoluzione, questo dovrebbe soddisfare di più.

8

Qui è completa soulution con l'aiuto personalizzato handler (quasi tutto il codice da @Adaephon risposta):

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() 
+1

Probabilmente è meglio usare 'parser.add_argument ('-h', '--help', action = _HelpAction, help = 'mostra questo messaggio di aiuto ed esce')' per abbinare il 'default' argparse '--help' opzione. – asmeurer

0

Sono stato anche in grado di stampare un breve aiuto per i comandi che utilizzano _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)) 
Problemi correlati