Отображать справочное сообщение с помощью python argparse, когда скрипт вызывается без каких-либо аргументов

Это может быть просто. Предположим, у меня есть программа, которая использует argparse для обработки аргументов / параметров командной строки. Следующее напечатает «справочное» сообщение:

./myprogram -h

or:

./myprogram --help

Но если я запускаю сценарий без каких-либо аргументов, он ничего не делает. Я хочу, чтобы он отображал сообщение об использовании, когда оно вызывается без аргументов. Как это сделать?


person musashiXXX    schedule 28.10.2010    source источник


Ответы (16)


Этот ответ исходит от Стивена Бетхарда, в группах Google . Я публикую его здесь, чтобы облегчить доступ людям без учетной записи Google.

Вы можете изменить поведение метода error по умолчанию:

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

Обратите внимание, что приведенное выше решение будет печатать справочное сообщение всякий раз, когда запускается метод error. Например, test.py --blah также напечатает справочное сообщение, если --blah недопустимый параметр.

Если вы хотите распечатать справочное сообщение только в том случае, если в командной строке не указаны аргументы, возможно, это все еще самый простой способ:

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

Обратите внимание, что parser.print_help() по умолчанию выводится на стандартный вывод. Как init_js предлагает, используйте parser.print_help(sys.stderr) для печати в stderr.

person unutbu    schedule 28.10.2010
comment
Да ... вот что меня интересовало, есть ли способ для argparse справиться с этим сценарием. Спасибо! - person musashiXXX; 28.10.2010
comment
Во втором решении я использую parser.print_usage() вместо parser.print_help() - в справочном сообщении указано использование, но оно более подробное. - person user2314737; 23.07.2015
comment
Я бы проголосовал за вторую часть ответа, но переопределение error() кажется мне ужасной идеей. Он служит другой цели, он не предназначен для удобного использования или помощи. - person Peterino; 10.01.2016
comment
@Peterino - переопределение происходит в дочернем классе, поэтому это не должно быть проблемой. Это явно. - person Marcel Wilson; 23.03.2016
comment
@unutbu ЗАМЕЧАТЕЛЬНО! Именно то, что мне нужно. Один вопрос, можно ли это применить и к подкомандам? Обычно я просто получаю «Пространство имен (output = None)». Как я могу легко вызвать ошибку для ВСЕХ подкоманд? Я хочу вызвать там ошибку. - person Jonathan Komar; 20.05.2016
comment
@ macmadness86: Навскидку не знаю ответа. Если вы опубликуете вопрос с кодом, показывающим вашу настройку и тип ошибки, которую вы хотите отловить, кто-то может указать вам способ. - person unutbu; 20.05.2016
comment
Во второй части рассмотрите возможность использования sys.exit(0) для получения того же статуса выхода, что и для ./myprogram -h - person Tomas; 08.05.2017
comment
sys.exit(0) означает, что программа завершилась успешно. sys.exit(1) указывает, что программа завершилась с ошибкой. - person unutbu; 08.05.2017
comment
Если вы хотите получить справочное сообщение в виде строки, а не печатать его напрямую: help_message = parser.format_help(). - person Pius Raeder; 13.12.2017
comment
print_help() и print_usage() оба могут принимать поток в качестве параметра. если вы хотите быть более согласованным с остальной обработкой ошибок в argparse, при ошибке можно было бы сделать parser.print_help(sys.stderr). Я считаю, что это хороший этикет для пользовательского интерфейса. - person init_js; 26.02.2018
comment
Я думаю, что иногда пользователь мог вводить данные из stdin. В таком случае я бы написал could следующим образом: `if len (sys.argv) == 1 and sys.stdin.isatty (): parser.print_help (sys.stderr) sys.exit (1) ` - person Medhat; 30.06.2019
comment
@Peterino: error() буквально предназначен для печати удобных сообщений об использовании. - person nemetroid; 14.10.2020
comment
люди без аккаунта гугл? это так 2010 .... - person Ulf Gjerdingen; 26.02.2021

Вместо написания класса можно использовать try / except

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

Плюс в том, что рабочий процесс более понятен, и вам не нужен класс-заглушка. Обратной стороной является то, что первая строка «использования» печатается дважды.

Для этого потребуется как минимум один обязательный аргумент. Без обязательных аргументов допустимо нулевое значение аргументов в командной строке.

person vacri    schedule 27.03.2015
comment
я тоже, я предпочитаю это принятому ответу. Добавление класса излишне печатать справку, когда аргументы неожиданные. Позвольте отличному модулю argparse обрабатывать случаи ошибок за вас. - person Nicole Finnie; 31.08.2016
comment
Этот код выводит справку 2 раза, если используется флаг -h, и ненужную справку, если используется флаг --version. Чтобы смягчить эти проблемы, вы можете проверить тип ошибки следующим образом: except SystemExit as err: if err.code == 2: parser.print_help() - person pkowalczyk; 26.02.2017
comment
Почему бы просто не поймать AttributeError? Не должно быть проблем с флагом -h или --version. - person Q. Qiao; 04.11.2020

С argparse вы можете:

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)
person cgseller    schedule 28.03.2015
comment
Это должно произойти до вызова parser.parse_args() - person Bob Stein; 28.08.2015

Если вы связываете функции по умолчанию для (под) парсеров, как указано в разделе add_subparsers, вы можете просто добавить его как действие по умолчанию:

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

Добавьте try-except, если вы вызываете исключения из-за отсутствия позиционных аргументов.

person AManOfScience    schedule 15.11.2016
comment
Этот ответ так недооценен. Просто и очень хорошо работает с суб-парсерами. - person orodbhen; 30.04.2018
comment
Отличный ответ! Единственное изменение, которое я сделал, - это использование лямбды без параметра. - person boh717; 26.11.2019

Если у вас есть аргументы, которые необходимо указать для запуска сценария, используйте параметр required для ArgumentParser, как показано ниже: -

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

parse_args () сообщит об ошибке, если скрипт запущен без аргументов.

person pd321    schedule 17.06.2016
comment
Это простейшее решение, которое также будет работать с указанными недопустимыми параметрами. - person Steve Scherer; 09.09.2016
comment
Согласовано. Я думаю, что всегда лучше использовать встроенные возможности парсера аргументов, чем писать какой-то дополнительный обработчик. - person Christopher Hunter; 22.03.2018
comment
Я не уверен, что это отвечает на актуальный вопрос. OP хочет, чтобы напечаталось справочное сообщение, если не указаны аргументы. Этот ответ включает добавление обязательного необязательного аргумента, который не был запрошен. В лучшем случае будет напечатано только сообщение об использовании. - person Jacob Pavlock; 06.08.2020

Самым чистым решением будет передача аргумента по умолчанию вручную, если в командной строке ничего не указано:

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

Полный пример:

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

Это напечатает полную справку (а не краткое использование), если она вызывается без аргументов.

person Ievgen Popovych    schedule 22.11.2017

Закидываем сюда мою версию:

import argparse

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

Вы можете заметить parser.exit - я в основном так делаю, потому что он сохраняет строку импорта, если это была единственная причина для sys в файле ...

person pauricthelodger    schedule 28.09.2015
comment
К сожалению, parser.parse_args () завершится, если позиционный аргумент отсутствует. Так что это работает только при использовании необязательных аргументов. - person Marcel Wilson; 23.03.2016
comment
@MarcelWilson, это действительно так - хороший улов! Я подумаю, как это изменить. - person pauricthelodger; 24.03.2016
comment
not vars(args) может не работать, если аргументы имеют default метод. - person funkid; 22.11.2019

Есть пара однострочников с sys.argv[1:] (очень распространенная идиома Python для обозначения аргументов командной строки, являющаяся sys.argv[0] именем сценария), которые могут выполнять эту работу.

Первый не требует пояснений, чистый и питонический:

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

Второй вариант немного посложнее. Комбинируя ранее оцененный факт, что пустой список False с эквивалентами True == 1 и False == 0, вы получаете следующее:

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

Может быть, слишком много скобок, но довольно ясно, если был выбран предыдущий аргумент.

_, *av = sys.argv
args = parser.parse_args([None, ['-h']][not av])
person Nuno André    schedule 25.07.2018

parser.print_help()
parser.exit()

Метод parser.exit также принимает status (код возврата) и значение message (включите завершающий перевод строки самостоятельно!).

самоуверенный пример, :)

#!/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=64, message="\nSomething went wrong, --example condition was not set\n")


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

Примеры звонков:

$ 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

Something went wrong, --example condition was not set
64
$ python3 ~/helloworld.py --example; echo $?
0
person ThorSummoner    schedule 14.12.2017

Вот еще один способ сделать это, если вам нужно что-то гибкое, где вы хотите отображать справку, если переданы определенные параметры, вообще нет или более одного конфликтующего аргумента:

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

Ваше здоровье!

person radtek    schedule 20.12.2017
comment
Я думаю, вам было бы намного проще использовать subparsers или mutually_exclusive_group - person Tim Bray; 22.08.2019

При вызове метода add_subparsers сохраните первый позиционный аргумент в dest= и проверьте значение после инициализации argparse, например:

subparsers = parser.add_subparsers(dest='command')

И просто проверьте эту переменную:

if not args.command:
    parser.print_help()
    parser.exit(1)  # If exit() - exit code will be zero (no error)

Полный пример:

#!/usr/bin/env python

""" doc """

import argparse
import sys

parser = argparse.ArgumentParser(description=__doc__)
subparsers = parser.add_subparsers(dest='command',
                                   help='List of commands')

list_parser = subparsers.add_parser('list',
                                    help='List contents')
list_parser.add_argument('dir', action='store',
                         help='Directory to list')

create_parser = subparsers.add_parser('create',
                                      help='Create a directory')
create_parser.add_argument('dirname', action='store',
                           help='New directory to create')
create_parser.add_argument('--read-only', default=False, action='store_true',
                           help='Set permissions to prevent writing to the directory')

args = parser.parse_args()

if not args.command:
    parser.print_help()
    parser.exit(1)

print(vars(args))  # For debug
person METAJIJI    schedule 14.02.2021

Задайте свои позиционные аргументы с помощью nargs и проверьте, пусты ли позиционные аргументы.

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

Ссылка Python nargs

person zerocog    schedule 12.04.2017

Если в вашей команде пользователю нужно выбрать какое-то действие, используйте взаимоисключающую группу с required = True.

Это своего рода расширение ответа, данного 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')

Выход:

$ python3 a_test.py
использование: a_test.py [-h] (--batch pay_id | --list | --all)
a_test.py: ошибка: один из аргументов --batch --list - все обязательно

Это дает только базовую помощь. И некоторые другие ответы окажут вам полную помощь. Но, по крайней мере, ваши пользователи знают, что могут использовать -h

person Tim Bray    schedule 22.08.2019

Это нехорошо (также потому, что перехватывает все ошибки), но:

def _error(parser):
    def wrapper(interceptor):
        parser.print_help()

        sys.exit(-1)

    return wrapper

def _args_get(args=sys.argv[1:]):
    parser = argparser.ArgumentParser()

    parser.error = _error(parser)

    parser.add_argument(...)
    ...

Вот определение функции error класса ArgumentParser:

https://github.com/python/cpython/blob/276eb67c29d05a93fbc22eea5470282e73700d20/Lib/argparse.py#L2374

. Как видите, после подписи требуется два аргумента. Однако функции вне класса ничего не знают о первом аргументе: self, потому что, грубо говоря, это параметр для класса. (Я знаю, что вы знаете ...) Таким образом, просто передать свой self и message в _error(...) не могут (

def _error(self, message):
    self.print_help()

    sys.exit(-1)

def _args_get(args=sys.argv[1:]):
    parser = argparser.ArgumentParser()

    parser.error = _error
    ...
...

выведет:

...
"AttributeError: 'str' object has no attribute 'print_help'"

). Вы можете передать parser (self) в функции _error, вызвав ее:

def _error(self, message):
    self.print_help()

    sys.exit(-1)

def _args_get(args=sys.argv[1:]):
    parser = argparser.ArgumentParser()

    parser.error = _error(parser)
    ...
...

, но вы не хотите выходить из программы прямо сейчас. Затем верните его:

def _error(parser):
    def wrapper():
        parser.print_help()

        sys.exit(-1)

    return wrapper
...

. Тем не менее, parser не знает, что он был изменен, поэтому при возникновении ошибки он отправит причину ее (кстати, ее локализованный перевод). Ну тогда перехвати его:

def _error(parser):
    def wrapper(interceptor):
        parser.print_help()

        sys.exit(-1)

    return wrapper
...

. Теперь, когда возникает ошибка и parser сообщит ее причину, вы ее перехватите, посмотрите на это и ... выбросите.

person hesed    schedule 16.03.2020

Я предпочитаю, чтобы все было как можно проще, это отлично работает:

#!/usr/bin/env python3
Description = """Tool description"""
Epilog  = """toolname.py -a aflag -b bflag  with these combined it does blah"""
arg_parser = argparse.ArgumentParser(
    formatter_class=argparse.RawDescriptionHelpFormatter,
    description=Description, 
    epilog=Epilog,
)
    try:
        if len(sys.argv) == 1:
            arg_parser.print_help()
    except Exception as e:
        print(e)

Вот так я запускаю все свои инструменты, потому что всегда полезно включать несколько примеров.

person Nick    schedule 11.11.2020

Итак, действительно простой ответ. Большую часть времени с argparse вы проверяете, установлены ли параметры в любом случае, чтобы вызвать функцию, которая что-то делает.

Если параметров нет, просто оставьте в конце и распечатайте справку. Просто так и работает.

import argparse
import sys
parser = argparse.ArgumentParser()

group = parser.add_mutually_exclusive_group()
group.add_argument("--holidays", action='store_true')
group.add_argument("--people", action='store_true')

args=parser.parse_args()
if args.holidays:
    get_holidays()
elif args.people:
    get_people()
else:
    parser.print_help(sys.stderr)
person Tim Bray    schedule 09.07.2021