Дайте пользователю настраивать программу через интерфейс командной строки. Воспользуйтесь библиотекой argparse
для Python.
Разбираемся, как это сделать. Библиотека argparse
поможет принимать значение конфига в командной строке.
Но как использовать argparse
?
Вот вам четыре шага:
- Импортируйте библиотеку.
- Создайте парсер.
- Добавьте в него аргументы.
- Запустите
.parse_args()
.
Последний приведёт к объекту Namespace
. Он содержит простое свойство для каждого входного аргумента из терминала.
Для подробного разбора этапов посмотрите программу myls.py
. Она перечисляет файлы в текущей директории. Ниже – реализация без argparse
:
# myls.py import os import sys if len(sys.argv) > 2: print('YoВЫu have specified too many arguments') sys.exit() if len(sys.argv) < 2: print('You need to specify the path to be listed') sys.exit() input_path = sys.argv[1] if not os.path.isdir(input_path): print('The path specified does not exist') sys.exit() print('\n'.join(os.listdir(input_path)))
Результат:
$ python myls.py You need to specify the path to be listed $ python myls.py /mnt /proc /dev You have specified too many arguments $ python myls.py /mnt dir1 dir2
Скрипт работает, но вывод отличается от встроенной команды.
Улучшите код с argparse
:
# myls.py # Импорт библиотеки argparse import argparse import os import sys # Создание парсера my_parser = argparse.ArgumentParser(description='List the content of a folder') # Добавление аргументов my_parser.add_argument('Path', metavar='path', type=str, help='the path to list') # Выполнение метода parse_args() args = my_parser.parse_args() input_path = args.Path if not os.path.isdir(input_path): print('The path specified does not exist') sys.exit() print('\n'.join(os.listdir(input_path)))
Первое отличие – отсутствие условного оператора if
, который проверял бы аргументы. Библиотека берёт эту функцию на себя.
Мы импортировали argparse
, создали простой парсер с описанием программы и определили ожидаемые от пользователя аргументы. Последним делом запустили .parse_args()
для разбора аргументов на входе и получения объекта Namespace
, который содержит пользовательский ввод.
После запуска вы увидите, как всего четыре строчки меняют результат:
$ python myls.py usage: myls.py [-h] path myls.py: error: the following arguments are required: path
Отсутствует нужный аргумент пути, поэтому получим ошибку.
А также программа принимает флаг -h
, как в примере ниже:
$ python myls.py -h usage: myls.py [-h] path List the content of a folder positional arguments: path the path to list optional arguments: -h, --help show this help message and exit
Приложение отвечает на -h
выводом справки. И это не требует от нас никаких усилий!
Всего четыре строчки кода превратили переменную args
в объект Namespace
. Он содержит свойства для аргументов, которые юзер вводит в интерфейс командной строки.
Назовите программу
По умолчанию библиотека использует значение элемента sys.argv[0]
для наименования программы, что соответствует названию скрипта. Укажите имя ключевым словом prog
:
# Создание парсера my_parser = argparse.ArgumentParser(prog='myls', description='List the content of a folder')
Оно будет отображаться в справке:
$ python myls.py usage: myls [-h] path myls.py: error: the following arguments are required: path
Теперь программа называется myls
, а не myls.py
.
Выводите настраиваемую справку в интерфейс командной строки
По умолчанию argparse
создаёт помощь своего формата. Настройте её с помощью usage
:
# Создание парсера my_parser = argparse.ArgumentParser(prog='myls', usage='%(prog)s [options] path', description='List the content of a folder')
Во время выполнения токен %(prog)s
автоматически заменяется именем программы:
$ python myls.py usage: myls [options] path myls: error: too few arguments
И справка показывает другую строку использования, где опция -h
сменилась универсальными токеном [options]
.
Отображайте текст до и после аргументов справки
Используйте два ключевых слова для настройки текста до и после справки:
description
: описание до вывода помощиepilog
: текст после
Посмотрим, как работает epilog
:
# Создание парсера my_parser = argparse.ArgumentParser(description='List the content of a folder', epilog='Enjoy the program! :)')
Результат:
$ python myls.py -h usage: myls.py [-h] path List the content of a folder positional arguments: path the path to list optional arguments: -h, --help show this help message and exit Enjoy the program! :)
Задайте символ префикса
По умолчанию, тире – начинает необязательные аргументы. Измените его с помощью ключевого слова prefix_chars
:
# Создание парсера my_parser = argparse.ArgumentParser(description='List the content of a folder', epilog='Enjoy the program! :)', prefix_chars='/')
Программа стала поддерживать другой префикс, а справка претерпела изменения:
$ python myls.py usage: myls.py [/h] path myls.py: error: too few arguments
Она отображается аргументом /h
. Используйте это для разработки под Windows.
Символы префикса для файлов
Сохраняйте аргументы для сложных программ запуска в файле и загружайте из него. argparse
выполняет эту работу из коробки.
Для теста напишите программу:
# fromfile_example.py import argparse my_parser = argparse.ArgumentParser(fromfile_prefix_chars='@') my_parser.add_argument('a', help='a first argument') my_parser.add_argument('b', help='a second argument') my_parser.add_argument('c', help='a third argument') my_parser.add_argument('d', help='a fourth argument') my_parser.add_argument('e', help='a fifth argument') my_parser.add_argument('-v', '--verbose', action='store_true', help='an optional argument') # Выполнение parse_args() args = my_parser.parse_args() print('If you read this line it means that you have provided ' 'all the parameters')
При создании парсера мы задействовали ключевое слово fromfile_prefix_chars
.
Запуск без аргументов приведёт к ошибке:
$ python fromfile_example.py usage: fromfile_example.py [-h] [-v] a b c d e fromfile_example.py: error: the following arguments are required: a, b, c, d, e
Создайте args.txt
с параметрами в каждой строке:
first second third fourth fifth
У вас есть специальный символ префикса для указания файла с аргументами. Откройте интерфейс командной строки и запустите предыдущую программу:
$ python fromfile_example.py @args.txt If you read this line it means that you have provided all the parameters
Видим: argparse
считывает аргументы из файла args.txt
.
Разрешайте и запрещайте сокращения
Следующая программа выводит указанное вами значение для аргумента --input
:
# abbrev_example.py import argparse my_parser = argparse.ArgumentParser() my_parser.add_argument('--input', action='store', type=int, required=True) my_parser.add_argument('--id', action='store', type=int) args = my_parser.parse_args() print(args.input)
Посмотрите, как argparse
обрабатывает сокращения и вызывает программу несколько раз:
$ python abbrev_example.py --input 42 42 $ python abbrev_example.py --inpu 42 42 $ python abbrev_example.py --inp 42 42 $ python abbrev_example.py --in 42 42
А что случится, если передать аргумент --i 42
? Библиотека не сможет понять, передавать 42
аргументу --input
или --id
, и выведет сообщение об ошибке:
$ python abbrev_example.py --i 42 usage: abbrev_example.py [-h] --input INPUT [--id ID] abbrev_example.py: error: ambiguous option: --i could match --input, --id
Не нравится эта функция? Хотите заставить пользователей вводить полные названия опций? Просто отключите эту возможность ключевым словом allow_abbrev
со значением False
на этапе создания парсера:
# abbrev_example.py import argparse my_parser = argparse.ArgumentParser(allow_abbrev=False) my_parser.add_argument('--input', action='store', type=int, required=True) args = my_parser.parse_args() print(args.input)
Запустите код, и вы увидите, что сокращения недоступны:
$ python abbrev_example.py --inp 42 usage: abbrev_example.py [-h] --input INPUT abbrev_example.py: error: the following arguments are required: --input
Ошибка сообщает, что пользователь не указал параметр --input
потому, что программа не распознала сокращение --inp
.
Задавайте имена флагов и аргументов
Вы можете добавить в интерфейс командной строки два типа аргументов:
- позиционные
- необязательные
Позиционными командами оперирует программа.
В предыдущем примере path
– позиционный аргумент. Без него программа не работает. Они называются позиционными потому, что позиция определяет их функцию.
Рассмотрим cp
из Linux:
$ cp [OPTION]... [-T] SOURCE DEST
Первый позиционный аргумент после cp
– источник файла для копирования. Второй – место назначения копии.
Необязательные аргументы изменяют поведение команд во время выполнения. В примере с cp
необязательный аргумент флаг -r
заставляет команду копировать директории рекурсивно.
Два типа аргументов отличаются синтаксисом: необязательные начинаются с -
или --
.
Хотите добавить необязательный аргумент? Вызовите .add_argument()
и назовите новый аргумент, начиная с -
.
Измените myls.py
:
# myls.py # Импорт библиотеки argparse import argparse import os import sys # Создание парсера my_parser = argparse.ArgumentParser(description='List the content of a folder') # Добавление аргументов my_parser.add_argument('Path', metavar='path', type=str, help='the path to list') my_parser.add_argument('-l', '--long', action='store_true', help='enable the long listing format') # Выполнение parse_args() args = my_parser.parse_args() input_path = args.Path if not os.path.isdir(input_path): print('The path specified does not exist') sys.exit() for line in os.listdir(input_path): if args.long: # Упрощённый длинный список size = os.stat(os.path.join(input_path, line)).st_size line = '%10d %s' % (size, line) print(line)
Запустите и проверьте опцию -l
:
$ python myls.py -h usage: myls.py [-h] [-l] path List the content of a folder positional arguments: path the path to list optional arguments: -h, --help show this help message and exit -l, --long enable the long listing format
Теперь программа принимает, но не требует опцию -l
.
Задайте действие для аргумента
При добавлении необязательного аргумента можно указать действие. Задайте способ хранения значения в объекте Namespace
, который вы получите после выполнения .parse_args()
.
Некоторые действия предопределены и доступны для использования:
store
хранит входное значение в объектеNamespace
(действие по умолчанию).store_const
содержит постоянное значение, когда указаны соответствующие необязательные аргументы.store_true
хранит логическоеTrue
, когда указан соответствующий необязательный аргумент, иFalse
в других случаях.store_false
хранит логическоеFalse
, когда указан соответствующий необязательный аргумент, иTrue
в других случаях.append
содержит список, добавляя значение каждый раз, когда опция указана.append_const
хранит список, добавляя постоянное значение.count
хранитint
, равный количеству использования опции.help
выводит справку.version
показывает версию программы.
Рассмотрим вышеуказанные опции на следующем примере:
# actions_example.py import argparse my_parser = argparse.ArgumentParser() my_parser.version = '1.0' my_parser.add_argument('-a', action='store') my_parser.add_argument('-b', action='store_const', const=42) my_parser.add_argument('-c', action='store_true') my_parser.add_argument('-d', action='store_false') my_parser.add_argument('-e', action='append') my_parser.add_argument('-f', action='append_const', const=42) my_parser.add_argument('-g', action='count') my_parser.add_argument('-i', action='help') my_parser.add_argument('-j', action='version') args = my_parser.parse_args() print(vars(args))
Скрипт принимает необязательный аргумент для каждого типа действия выше и печатает значение аргументов, передаваемых через интерфейс командной строки. Протестируйте выполнением примера:
$ python actions_example.py {'a': None, 'b': None, 'c': False, 'd': True, 'e': None, 'f': None, 'g': None}
Видно, что без указаний аргументов, значения по умолчанию – None
.
Действие store
хранит передаваемое значение:
$ python actions_example.py -a 42 {'a': '42', 'b': None, 'c': False, 'd': True, 'e': None, 'f': None, 'g': None} $ python actions_example.py -a "test" {'a': 'test', 'b': None, 'c': False, 'd': True, 'e': None, 'f': None, 'g': None}
store_const
хранит определённую константу, когда предоставлены аргументы. В тесте мы указали аргумент b
, и значение args.b
стало 42
:
$ python actions_example.py -b {'a': None, 'b': 42, 'c': False, 'd': True, 'e': None, 'f': None, 'g': None}
Действие store_true
хранит логическое True
при наличии передаваемых аргументов и False
в остальных случаях. Нужно противоположное поведение? Воспользуйтесь действием store_false
:
$ python actions_example.py {'a': None, 'b': None, 'c': False, 'd': True, 'e': None, 'f': None, 'g': None} $ python actions_example.py -c {'a': None, 'b': None, 'c': True, 'd': True, 'e': None, 'f': None, 'g': None} $ python actions_example.py -d {'a': None, 'b': None, 'c': False, 'd': False, 'e': None, 'f': None, 'g': None}
Создавайте список всех переданных значений одним аргументом с помощью append
:
$ python actions_example.py -e me -e you -e us {'a': None, 'b': None, 'c': False, 'd': True, 'e': ['me', 'you', 'us'], 'f': None, 'g': None}
append_const
похоже на append
, но добавляет одно постоянное значение:
$ python actions_example.py -f -f {'a': None, 'b': None, 'c': False, 'd': True, 'e': None, 'f': [42, 42], 'g': None}
count
считает количество передач аргумента. Оно полезно в реализации уровня подробностей вывода программы. Можно определить уровень как -v
– меньше подробностей, чем -vvv
:
$ python actions_example.py -ggg {'a': None, 'b': None, 'c': False, 'd': True, 'e': None, 'f': None, 'g': 3} $ python actions_example.py -ggggg {'a': None, 'b': None, 'c': False, 'd': True, 'e': None, 'f': None, 'g': 5}
Действие version
просто показывает версию программы:
$ python actions_example.py -j 1.0
Ещё одна возможность: создавайте собственные действия. Для этого наследуйте класс argparse.Action
и реализуйте пару методов.
Следующий пример – настраиваемое действие store
, которое выводит больше подробностей, чем стандартное:
# custom_action.py import argparse class VerboseStore(argparse.Action): def __init__(self, option_strings, dest, nargs=None, **kwargs): if nargs is not None: raise ValueError('nargs not allowed') super(VerboseStore, self).__init__(option_strings, dest, **kwargs) def __call__(self, parser, namespace, values, option_string=None): print('Here I am, setting the ' \ 'values %r for the %r option...' % (values, option_string)) setattr(namespace, self.dest, values) my_parser = argparse.ArgumentParser() my_parser.add_argument('-i', '--input', action=VerboseStore, type=int) args = my_parser.parse_args() print(vars(args))
Результат выполнения:
$ python custom_action.py -i 42 Here I am, setting the values 42 for the '-i' option... {'input': 42}
Программа вывела линию прежде, чем задать значение 42
параметру -i
.
Задавайте количество значений для опции
По умолчанию парсер предполагает один параметр для каждого аргумента. Измените это поведение, указав другое количество значений ключевым словом nargs
.
Нужен аргумент, который принимает три значения? Укажите 3
в качестве значения nargs
во время добавления параметра в парсер:
# nargs_example.py import argparse my_parser = argparse.ArgumentParser() my_parser.add_argument('--input', action='store', type=int, nargs=3) args = my_parser.parse_args() print(args.input)
Теперь программа принимает три значения для параметра --input
:
$ python nargs_example.py --input 42 usage: nargs_example.py [-h] [--input INPUT INPUT INPUT] nargs_example.py: error: argument --input: expected 3 arguments $ python nargs_example.py --input 42 42 42 [42, 42, 42]
А значение переменной args.input
– это список с тремя значениями.
Ключевое слово nargs
также принимает:
?
: одно необязательное значение;*
: гибкое количество значений, которые собираются в список;+
: похоже на*
, но требует хотя бы одного значения;argparse.REMAINDER
: все значения, которые остаются в командной строке.
В следующей программе позиционный аргумент input
принимает одно значение. Если оно отсутствует, программа использует значение ключевого слово default
:
# nargs_example.py import argparse my_parser = argparse.ArgumentParser() my_parser.add_argument('input', action='store', nargs='?', default='my default value') args = my_parser.parse_args() print(args.input)
Выбирайте значение для аргумента input
. В данном случае будет использовано default
:
$ python nargs_example.py 'my custom value' my custom value $ python nargs_example.py my default value
Чтобы принять несколько значений и собрать их в список, укажите *
в качестве значения nargs
:
# nargs_example.py import argparse my_parser = argparse.ArgumentParser() my_parser.add_argument('input', action='store', nargs='*', default='my default value') args = my_parser.parse_args() print(args.input)
Этот код позволяет задавать гибкое число значений для ожидаемого аргумента:
$ python nargs_example.py me you us ['me', 'you', 'us'] $ python nargs_example.py my default value
Если вам нужно принять переменное количество значений и убедиться, что указано хотя бы одно значение, используйте +
в качестве значений nargs
:
# nargs_example.py import argparse my_parser = argparse.ArgumentParser() my_parser.add_argument('input', action='store', nargs='+') args = my_parser.parse_args() print(args.input)
Запустите программу без позиционных аргументов, и увидите точное сообщение об ошибке:
$ python nargs_example.py me you us ['me', 'you', 'us'] $ python nargs_example.py usage: nargs_example.py [-h] input [input ...] nargs_example.py: error: the following arguments are required: input
Напоследок представьте, что вам нужно захватить и отправить в список все оставшиеся аргументы, которые были указаны в командной строке. Для этого присвойте значение argparse.REMAINDER
ключевому слову nargs
:
# nargs_example.py import argparse my_parser = argparse.ArgumentParser() my_parser.add_argument('first', action='store') my_parser.add_argument('others', action='store', nargs=argparse.REMAINDER) args = my_parser.parse_args() print('first = %r' % args.first) print('others = %r' % args.others)
Посмотрите, что происходит: первое значение будет связано с первым параметром. Остальные – со вторым:
$ python nargs_example.py me you us first = 'me' others = ['you', 'us']
Оставшиеся значения собираются в список.
Установите значение по умолчанию в отсутствии аргумента
Вы уже знаете, что пользователь может не указывать необязательные аргументы в командной строке. Когда аргументы не указаны, соответствующее значение ставится в None
.
Но вы можете определить значение по умолчанию для аргумента:
# default_example.py import argparse my_parser = argparse.ArgumentParser() my_parser.add_argument('-a', action='store', default='42') args = my_parser.parse_args() print(vars(args))
Запустите программу без опции -a
, и вы получите:
$ python default_example.py {'a': '42'}
Теперь опция -a
имеет значение 42
, хотя вы не указывали это явно в командной строке.
Установите тип аргумента
По умолчанию все входные значения обрабатываются как строки. У вас есть возможность определять тип соответствующего свойства объекта Namespace
, которое вы получаете после вызова .parse_args()
. Воспользуйтесь ключевым словом type
:
# type_example.py import argparse my_parser = argparse.ArgumentParser() my_parser.add_argument('-a', action='store', type=int) args = my_parser.parse_args() print(vars(args))
Указывая значение типа int
для аргумента, вы говорите argparse
, что свойство .a
объекта Namespace
должно быть int
вместо string
:
$ python type_example.py -a 42 {'a': 42}
Теперь значение аргумента проверяется во время выполнения. Если значение не подходит, выводится чёткая ошибка:
$ python type_example.py -a "that's a string" usage: type_example.py [-h] [-a A] type_example.py: error: argument -a: invalid int value: "that's a string"
Установите допустимые значения для определённого аргумента
Ещё одна интересная особенность библиотеки argparse
в Python. Предоставьте список принимаемых значений на стадии добавления новой опции:
# choices_ex.py import argparse my_parser = argparse.ArgumentParser() my_parser.add_argument('-a', action='store', choices=['head', 'tail']) args = my_parser.parse_args()
Хотите принимать числовые значения? Используйте range()
для определения диапазона принимаемых значений:
# choices_ex.py import argparse my_parser = argparse.ArgumentParser() my_parser.add_argument('-a', action='store', type=int, choices=range(1, 5)) args = my_parser.parse_args() print(vars(args))
Тогда значение, указанное вами в командной строке, автоматически сравнивается со списком допустимых значений:
$ python choices_ex.py -a 4 {'a': 4} $ python choices_ex.py -a 40 usage: choices_ex.py [-h] [-a {1,2,3,4}] choices_ex.py: error: argument -a: invalid choice: 40 (choose from 1, 2, 3, 4)
Если его там нет, вы получите сообщение об ошибке.
Установите необходимость аргумента
Хотите заставить пользователя вводить необязательный аргумент? Используйте ключевое слово required
:
# required_example.py import argparse my_parser = argparse.ArgumentParser() my_parser.add_argument('-a', action='store', choices=['head', 'tail'], required=True) args = my_parser.parse_args() print(vars(args))
Установите значение required
в True
, и пользователь должен будет указывать значение для этого аргумента:
$ python required_example.py usage: required_example.py [-h] -a {head,tail} required_example.py: error: the following arguments are required: -a $ python required_example.py -a head {'a': 'head'}
Помните, что требование значения для необязательного аргумента считается плохой практикой. Пользователь не ожидает, что нужно указывать значение для необязательного аргумента.
Показывайте краткое описание аргумента
Улучшайте справку argparse
описаниями аргументов:
# help_example.py import argparse my_parser = argparse.ArgumentParser() my_parser.add_argument('-a', action='store', choices=['head', 'tail'], help='set the user choice to head or tail') args = my_parser.parse_args() print(vars(args))
Пользователь увидит больше подробностей в справке:
$ python help_example.py -h usage: help_example.py [-h] [-a {head,tail}] optional arguments: -h, --help show this help message and exit -a {head, tail} set the user choice to head or tail
Такая справка делает программу удобней.
Комментарии