Exibir mensagem de ajuda com Python argparse quando o script é chamado sem argumentos

StackOverflow https://stackoverflow.com/questions/4042452

  •  27-09-2019
  •  | 
  •  

Pergunta

Isso pode ser simples. Suponha que eu tenha um programa que use o ArgParse para processar argumentos/opções da linha de comando. O seguinte imprimirá a mensagem 'Ajuda':

./myprogram -h

ou:

./myprogram --help

Mas, se eu executar o script sem nenhum argumento, ele não fará nada. O que eu quero fazer é exibir a mensagem de uso quando é chamada sem argumentos. Como isso é feito?

Foi útil?

Solução

Esta resposta vem de Steven Bethard no Google Grupos. Estou repositando aqui para facilitar o acesso às pessoas sem uma conta do Google.

Você pode substituir o comportamento padrão do error método:

import argparse
import sys

class MyParser(argparse.ArgumentParser):
    def error(self, message):
        sys.stderr.write('error: %s\n' % message)
        self.print_help()
        sys.exit(2)

parser = MyParser()
parser.add_argument('foo', nargs='+')
args = parser.parse_args()

Observe que a solução acima imprimirá a mensagem de ajuda sempre que o errorO método é acionado. Por exemplo, test.py --blah Imprimirá a mensagem de ajuda também se --blah não é uma opção válida.

Se você deseja imprimir a mensagem de ajuda somente se nenhum argumento for fornecido na linha de comando, talvez essa ainda seja a maneira mais fácil:

import argparse
import sys

parser=argparse.ArgumentParser()
parser.add_argument('foo', nargs='+')
if len(sys.argv)==1:
    parser.print_help(sys.stderr)
    sys.exit(1)
args=parser.parse_args()

Observe que parser.print_help() Imprime para stdout por padrão. Como init_js sugere, usar parser.print_help(sys.stderr) Para imprimir para Stderr.

Outras dicas

Em vez de escrever uma aula, uma tentativa/exceto pode ser usada em vez disso

try:
    options = parser.parse_args()
except:
    parser.print_help()
    sys.exit(0)

A vantagem é que o fluxo de trabalho é mais claro e você não precisa de uma classe de stub. A desvantagem é que a primeira linha de 'uso' é impressa duas vezes.

Isso precisará de pelo menos um argumento obrigatório. Sem argumentos obrigatórios, o fornecimento de zero args na linha de comando é válido.

Com o argparse que você poderia fazer:

parser.argparse.ArgumentParser()
#parser.add_args here

#sys.argv includes a list of elements starting with the program
if len(sys.argv) < 2:
    parser.print_usage()
    sys.exit(1)

Se você tiver argumentos que devem ser especificados para que o script seja executado - use o requeridos Parâmetro para ArgumentParser, como mostrado abaixo:-

parser.add_argument('--foo', required=True)

parse_args () relatará um erro se o script for executado sem argumentos.

Se você associar funções padrão para (sub) analisadores, como é mencionado em add_subparsers, você pode simplesmente adicioná -lo como a ação padrão:

parser = argparse.ArgumentParser()
parser.set_defaults(func=lambda x: parser.print_usage())
args = parser.parse_args()
args.func(args)

Adicione o Excepto de Except se você aumentar as exceções devido à falta de argumentos posicionais.

Jogando minha versão na pilha aqui:

import argparse

parser = argparse.ArgumentParser()
args = parser.parse_args()
if not vars(args):
    parser.print_help()
    parser.exit(1)

Você pode notar o parser.exit - Eu faço principalmente assim porque salva uma linha de importação se essa foi a única razão para sys no arquivo ...

A solução mais limpa será aprovar manualmente o argumento padrão se nenhum foi fornecido na linha de comando:

parser.parse_args(args=None if sys.argv[1:] else ['--help'])

Exemplo completo:

import argparse, sys

parser = argparse.ArgumentParser()
parser.add_argument('--host', default='localhost', help='Host to connect to')
# parse arguments
args = parser.parse_args(args=None if sys.argv[1:] else ['--help'])

# use your args
print("connecting to {}".format(args.host))

Isso imprimirá ajuda completa (não uso curto) se chamado sem argumentos.

Há um par de frases com sys.argv[1:] (um idioma de um python muito comum para referir os argumentos da linha de comando, sendo sys.argv[0] o nome do roteiro) que pode fazer o trabalho.

O primeiro é auto-explicativo, limpo e pitônico:

args = parser.parse_args(None if sys.argv[1:] else ['-h'])

O segundo é um pouco mais hacker. Combinando o fato avaliado anteriormente que uma lista vazia é False com o True == 1 e False == 0 Equivalências você entende:

args = parser.parse_args([None, ['-h']][not sys.argv[1:]])

Talvez muitos colchetes, mas bastante claro se uma seleção de argumentos anterior foi feita.

_, *av = sys.argv
args = parser.parse_args([None, ['-h']][not av])
parser.print_help()
parser.exit()

o parser.exit Método também aceita um status (ReturnCode) e um message Valor (inclua uma linha nova à direita!).

um exemplo opinativo :)

#!/usr/bin/env python3

""" Example argparser based python file
"""

import argparse

ARGP = argparse.ArgumentParser(
    description=__doc__,
    formatter_class=argparse.RawTextHelpFormatter,
)
ARGP.add_argument('--example', action='store_true', help='Example Argument')


def main(argp=None):
    if argp is None:
        argp = ARGP.parse_args()  # pragma: no cover

    if 'soemthing_went_wrong' and not argp.example:
        ARGP.print_help()
        ARGP.exit(status=128, message="\nI just don't know what went wrong, maybe missing --example condition?\n")


if __name__ == '__main__':
    main()  # pragma: no cover

Exemplo de chamadas:

$ python3 ~/helloworld.py; echo $?
usage: helloworld.py [-h] [--example]

 Example argparser based python file

optional arguments:
  -h, --help  show this help message and exit
  --example   Example Argument

I just don't know what went wrong, maybe missing --example condition?
128
$ python3 ~/helloworld.py --example; echo $?
0

Defina seus argumentos posicionais com NARGs e verifique se os args posicionais estão vazios.

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('file', nargs='?')
args = parser.parse_args()
if not args.file:
    parser.print_help()

Referência Python Nargs

Aqui está outra maneira de fazê -lo, se você precisar de algo flexível, onde deseja exibir ajuda se params específicos forem passados, nenhum ou mais de 1 arg conflitante:

import argparse
import sys

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-d', '--days', required=False,  help="Check mapped inventory that is x days old", default=None)
    parser.add_argument('-e', '--event', required=False, action="store", dest="event_id",
                        help="Check mapped inventory for a specific event", default=None)
    parser.add_argument('-b', '--broker', required=False, action="store", dest="broker_id",
                        help="Check mapped inventory for a broker", default=None)
    parser.add_argument('-k', '--keyword', required=False, action="store", dest="event_keyword",
                        help="Check mapped inventory for a specific event keyword", default=None)
    parser.add_argument('-p', '--product', required=False, action="store", dest="product_id",
                        help="Check mapped inventory for a specific product", default=None)
    parser.add_argument('-m', '--metadata', required=False, action="store", dest="metadata",
                        help="Check mapped inventory for specific metadata, good for debugging past tix", default=None)
    parser.add_argument('-u', '--update', required=False, action="store_true", dest="make_updates",
                        help="Update the event for a product if there is a difference, default No", default=False)
    args = parser.parse_args()

    days = args.days
    event_id = args.event_id
    broker_id = args.broker_id
    event_keyword = args.event_keyword
    product_id = args.product_id
    metadata = args.metadata
    make_updates = args.make_updates

    no_change_counter = 0
    change_counter = 0

    req_arg = bool(days) + bool(event_id) + bool(broker_id) + bool(product_id) + bool(event_keyword) + bool(metadata)
    if not req_arg:
        print("Need to specify days, broker id, event id, event keyword or past tickets full metadata")
        parser.print_help()
        sys.exit()
    elif req_arg != 1:
        print("More than one option specified. Need to specify only one required option")
        parser.print_help()
        sys.exit()

    # Processing logic here ...

Felicidades!

Se o seu comando é algo em que um usuário precisa escolher alguma ação, use um grupo mutuamente exclusivo com obrigatório = true.

Isso é uma espécie de extensão da resposta dada pelo PD321.

import argparse

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--batch", action='store', type=int,  metavar='pay_id')
group.add_argument("--list", action='store_true')
group.add_argument("--all", action='store_true', help='check all payments')

args=parser.parse_args()

if args.batch:
    print('batch {}'.format(args.batch))

if args.list:
    print('list')

if args.all:
    print('all')

Resultado:

$ python3 a_test.py
Uso: a_test.py [-h] (-batch pay_id |--list | --l)
a_test.py: erro: um dos argumentos -lote -list --l é necessário

Isso só dá a ajuda básica. E algumas das outras respostas lhe darão a ajuda total. Mas pelo menos seus usuários sabem que podem fazer -h

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top