Вывести справочное сообщение с python argparse, когда скрипт вызывается без аргументов

226

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

./myprogram -h

или:

./myprogram --help

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

musashiXXX
источник

Ответы:

273

Этот ответ приходит от Стивена Бетарда из групп 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.

unutbu
источник
Да ... это то, что меня интересует, есть ли способ для argparse справиться с этим сценарием. Спасибо!
musashiXXX
6
Во втором решении, которое я использую parser.print_usage()вместо parser.print_help()- сообщение помощи включает использование, но оно более многословно.
user2314737
5
Я бы проголосовал за вторую часть ответа, но переопределение error()кажется мне ужасной идеей. Он служит для другой цели, он не предназначен для печати в дружественной форме или помощи.
Петерино
@Peterino - переопределение происходит в дочернем классе, поэтому это не должно быть проблемой. Это явно.
Марсель Уилсон
1
@unutbu ЧУДЕСНО! Именно то, что мне было нужно. Один вопрос, это можно применить и к подкомандам? Я обычно просто получаю `` Namespace (output = None) `. Как я могу легко вызвать ошибку во ВСЕХ подкомандах? Я хотел бы вызвать ошибку там.
Джонатан Комар
56

Вместо написания класса можно использовать попытку / исключение

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

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

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

Vacri
источник
я тоже, я предпочитаю это принятому ответу. Добавление класса излишне для печати справки, когда аргументы являются неожиданными. Пусть отличный модуль argparse обрабатывает ошибки для вас.
Николь Финни
7
Этот код печатает справку 2 раза, если -hиспользуется флаг, и ненужные печатает справку, если --versionиспользуется флаг. Чтобы смягчить эти проблемы, вы можете проверить тип ошибки следующим образом:except SystemExit as err: if err.code == 2: parser.print_help()
pkowalczyk
25

С 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)
cgseller
источник
5
Это должно прийти до звонкаparser.parse_args()
Боб Стейн
18

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

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

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

pd321
источник
2
Это простейшее решение, и оно будет работать с указанными неверными параметрами.
Стив Шерер
1
Согласовано. Я думаю, что всегда лучше использовать встроенные возможности парсера аргументов, чем писать какой-то дополнительный обработчик.
Кристофер Хантер
18

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

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

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

AManOfScience
источник
1
Этот ответ так недооценен. Просто и очень хорошо работает с подпарсерами.
orodbhen
Отличный ответ! Единственное изменение, которое я сделал, - использование лямбды без параметров.
boh717
12

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

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

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

Евгений Попович
источник
2
sys.argv[1:]это очень распространенная идиома. Вижу parser.parse_args(None if sys.argv[1:] else ['-h'])идиоматичнее и чище.
Нуно Андре
1
@ NunoAndré спасибо - обновил ответ. На самом деле чувствует себя более питонным.
Евгений Попович
10

Бросаю мою версию в кучу здесь:

import argparse

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

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

pauricthelodger
источник
parser.exit (1) это хорошо! Хорошее дополнение.
cgseller
4
К сожалению, parser.parse_args () завершится, если позиционный аргумент отсутствует. Так что это работает только при использовании необязательных аргументов.
Марсель Уилсон
1
@MarcelWilson, это действительно - хороший улов! Я подумаю, как это изменить.
pauricthelodger
not vars(args)может не работать, когда аргументы имеют defaultметод.
Funkid
5

Есть пара однострочников с 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])
Нуно Андре
источник
1
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=128, message="\nI just don't know what went wrong, maybe missing --example condition?\n")


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

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

$ python3 ~ / helloworld.py; echo $?
использование: helloworld.py [-h] [--example]

 Пример файла Python на основе argparser

необязательные аргументы:
  -h, --help показать это справочное сообщение и выйти
  - Пример Пример Аргумент

Я просто не знаю, что пошло не так, может быть, не хватает - пример условия?
128
$ python3 ~ / helloworld.py --example; echo $?
0
ThorSummoner
источник
0

Установите ваши позиционные аргументы с помощью nargs и проверьте, не являются ли позиционные аргументы пустыми.

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

Ссылки на Python Nargs

zerocog
источник
0

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

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

Ура!

radtek
источник
Я думаю, вам было бы намного проще использовать подпарсеры или взаимно исключающую группу
Тим Брей
0

Если в вашей команде пользователю нужно выбрать какое-либо действие, используйте взаимоисключающую группу с 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

Тим Брей
источник
0

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

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отправит причину, вы перехватите ее, посмотрите на это и ... выбросите.

Максим Темный
источник