Argparse — парсим аргументы и параметры командной строки с легкостью

в 2:58, , рубрики: parser, python, метки: ,

Начиная с версии Python 2.7, в набор стандартных библиотек была включена библиотека argparse для обработки аргументов (параметров, ключей) командной строки. Хотелось бы остановить на ней Ваше внимание.

Для начала рассмотрим, что интересного предлагает argparse.

Argparse — это изящный инструмент для:

  • анализа аргументов sys.argv;
  • конвертирования строковых аргументов в объекты Вашей программы и работа с ними;
  • форматирования и вывода информативных подсказок;
  • многого другого.

Одним из аргументов противников включения argparse в Python был довод о том, что в стандартных модулях и без этого содержится две библиотеки для семантической обработки (парсинга) параметров командной строки. Однако, как заявляют разработчики argparse, библиотеки getopt и optparse уступают argparse по нескольким причинам:

  • обладая всей полнотой действий с обычными параметрами командной строки, они не умеют обрабатывать позиционные аргументы (positional arguments). Позиционные аргументы — это аргументы, влияющие на работу программы, в зависимости от порядка, в котором они в эту программу передаются. Простейший пример — программа cp, имеющая минимум 2 таких аргумента («cp source destination»).
  • argparse дает на выходе более качественные сообщения о подсказке при минимуме затрат (в этом плане при работе с optparse часто можно наблюдать некоторую избыточность кода);
  • argparse дает возможность программисту устанавливать для себя, какие символы являются параметрами, а какие нет. В отличие от него, optparse считает опции с синтаксисом наподобии "-pf, -file, +rgb, /f и т.п. «внутренне противоречивыми» и «не поддерживается optpars'ом и никогда не будет»;
  • argparse даст Вам возможность использовать несколько значений переменных у одного аргумента командной строки (nargs);
  • argparse поддерживает субкоманды (subcommands). Это когда основной парсер отсылает к другому (субпарсеру), в зависимости от аргументов на входе.

Для начала работы с argparse необходимо задать парсер:

ap.py:
import argparse
parser = argparse.ArgumentParser(description='Great Description To Be Here')

Далее, парсеру стоит указать, какие объекты Вы от него ждете. В частном случае, это может выглядеть так:

parser.add_argument('-n', action='store', dest='n', help='Simple value')

Если действие (action) для данного аргумента не задано, то по умолчанию он будет сохраняться (store) в namespace, причем мы также можем указать тип этого аргумента (int, boolean и тд). Если имя возвращаемого аргумента (dest) задано, его значение будет сохранено в соответствующем атрибуте namespace.

В нашем случае:
print parser.parse_args(['-n', '3'])

Namespace(n='3')

print parser.parse_args([])

Namespace(n=None)

print parser.parse_args(['-a', '3'])
error: unrecognized arguments: -a 3

Простой пример программы, возводящей в квадрат значение позиционного аргумента (square) и формирующей вывод в зависимости от аргумента опционального (-v):

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbose", action="store_true",
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbose:
print("the square of {} equals {}".format(args.square, answer))
else:
print(answer)

Остановимся на действиях (actions). Они могут быть следующими:
— store: возвращает в пространство имен значение (после необязательного приведения типа). Как уже говорилось, store — действие по умолчанию;

— store_const: в основном используется для флагов. Либо вернет Вам значение, указанное в const, либо (если ничего не указано), None. Пример:

parser.add_argument('--LifetheUniverseandEverything', action='store_const', const=42)
print parser.parse_args(['--LifetheUniverseandEverything'])

Namespace(LifetheUniverseandEverything=42)

— store_true / store_false: аналог store_const, но для булевых True и False;

— append: возвращает список путем добавления в него значений агрументов. Пример:
parser.add_argument('--l', action='append')
print parser.parse_args('--l a --l b --l Y'.split())

Namespace(l=['abY'])

— append_const: возвращение значения, определенного в спецификации аргумента, в список. Пока у меня не было кейса, в котором понадобился бы append_const.

— count: как следует из названия, считает, сколько раз встречается значение данного аргумента. Пример:

parser.add_argument('--verbose', '-v', action='count')
print parser.parse_args('-vvv'.split())

Namespace(verbose=3)

В зависимости от переданного в конструктор парсера аргумента add_help (булевого типа), будет определяться, включать или не включать в стандартный вывод по ключам ['-h', '--help'] сообщения о помощи. То же самое будет иместь место с аргументом version (строкового типа), ключи по умолчанию: ['-v', '--version']. При запросе помощи или номера версии, дальнейшее выполнение прерывается.

parser = argparse.ArgumentParser(add_help=True, version='4.0')

Иногда необходимо определить некий набор параметров командной строки, который будет распространяться на все парсеры Вашей программы. В данном случае часто приводят пример необходимости авторизации:

parent_parser = argparse.ArgumentParser(add_help=False)
parent_parser.add_argument('--user', action="store")
parent_parser.add_argument('--password', action="store")
child_parser = argparse.ArgumentParser(parents=[parent_parser])
child_parser.add_argument('--show_all', action="store_true")
print child_parser.parse_args(['--user', 'guest'])

Namespace(password=None, show_all=False, user='guest')

Обратите внимание, что родительский парсер создается с параметром add_help=False. Это сделано потому, что каждый парсер будет честно стараться добавить свой обработчик ключа '-h', чем вызовет конфликтную ситуацию. Отсюда возникает вопрос, что делать, если у вашего дочернего парсера имеются те же ключи, что и у родительского и при этом вы хотите их использовать без всяких конфликтов? Делается это простым добавлением аргумента conflict_handler:

parent_parser = argparse.ArgumentParser(add_help=False)
parent_parser.add_argument('--user', action="store")
parent_parser.add_argument('--password', action="store")
child_parser = argparse.ArgumentParser(parents=[parent_parser], conflict_handler='resolve')
child_parser.add_argument('--user', action="store", default="Guest")
print child_parser.parse_args()

Namespace(password=None, user='Guest')

Помимо вышеописанного подхода к обработке команд разного уровня, существует еще и альтернативный подход, позволяющий объединить обработку всех команд в одной программе, используя субпарсеры (subparsers). Лучше всего сам за себя расскажет пример:

ap.py
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help='List of commands')
# A list command
list_parser = subparsers.add_parser('list', help='List contents')
list_parser.add_argument('dirname', action='store', help='Directory to list')
# A create command
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',
)

Вот что выдаст программа с ключом '-h':

usage: ap.py [-h] {list,create} ...

positional arguments:
{list,create} list of commands
list List contents
create Create a directory

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

В примере можно отметить следующие вещи:
1. позиционные аргументы list, create, передаваемые программе — по сути субпарсеры;
2. аргумент '--read-only' субпарсера create_parser — опциональный, dir_name — необходимый для обоих субпарсеров;
3. предусмотрена справка (помощь) как для парсера, так и для каждго из субпарсеров.

Вообще, argparse — довольно мощная и легкая библиотека, предоставляющая, на мой взгляд, очень удобный интерфейс для работы с параметрами командной строки. В следующий раз постараюсь охватить такие вопросы, как файловые аргументы (для более продвинутой работы с файлами), переменные списки аргументов, группы аргументов и более подробно остановиться на типизации аргументов.
Спасибо за внимание.

Список литературы:
Why aren't getopt and optparse enough (PEP 389)
Argparse positional and optional arguments
Argparse documentation
Parser for command-line options, arguments and sub-commands

Автор: w1cked

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js