Простое руководство по аргументам командной строки Python
Одна из сильнейших сторон Python — способность выполнять любые задачи. Его стандартная библиотека предоставляет достаточно функций для написания множества полезных сценариев и инструментов. А для получения больших возможностей можно просто воспользоваться pip install.
Почти каждый современный ЯП способен принимать аргументы из командной строки. Это очень важная функция, поскольку она допускает динамический ввод данных от пользователей, независимо от того, написали они программу или нет.
Python содержит несколько библиотек , благодаря которым код может принимать введенные пользователем данные из команды, таких как sys.argv, getopt и argparse. На данный момент argparse — наилучший и наиболее распространенный вариант.
Библиотека Python argparse была выпущена как часть стандартной библиотеки вместе с Python 3.2. После этого релиза в силу своей популярности она была интегрирована в Python 2.7 и во все будущие версии Python, быстро став золотым стандартом для работы с аргументами командной строки. Она предоставляет следующие функции:
- Возможность настройки справочных сообщений и документации для аргументов командной строки.
- Установка значений по умолчанию для аргументов в чистом и читабельном виде.
- Наличие заданного количества опций для определенного аргумента командной строки.
- Поддержка переменного количества параметров для одного аргумента.
- Возможность автоматически применять действие или функцию к вводу по определенным указаниям.
Проведем небольшую экскурсию по библиотеке argparse Python.
Начальная установка
Откройте пустой сценарий Python. Библиотека argparse встроена в Python, соответственно процедура установки не потребуется. На фрагменте кода ниже показано, как создать начальную установку для argparse:
parser = argparse.ArgumentParser(description=’A tutorial of argparse!’)
В этом коде есть 3 важных компонента:
- Импорт argparse.
- Создание парсера аргументов (с описанием).
- Парсинг аргументов командной строки.
На втором этапе мы создаем объект parser, с помощью которого можно добавлять аргументы командной строки вместе с опциями для каждого из них. На третьем этапе запускается функция, которая извлекает введенные пользователем аргументы из командной строки.
Прелесть argparse заключается в том, что она автоматически работает с командной строкой с помощью этой простой настройки. Запустите программу с —help в конце и argparse выведет описание руководства по argparse!
Теперь добавим аргумент командной строки с именем «a», как показано во фрагменте кода ниже. Чтобы передать аргумент командной строки в сценарий Python, запустите его с помощью python3 argparse_2.py —a=5:
Обратите внимание на использование функции .add_argument() для передачи «a» в качестве аргумента командной строки. Чтобы получить доступ к переменной, полученной из a, используем args.a.
Следует отметить, что без указания значения для a в командной строке args.a будет None. Чтобы обойти это, указываем значение по умолчанию для a в парсере аргумента, как показано ниже. Обратите внимание, что на этот раз также добавлено описание.
В данном случае, если мы не передадим значение для a через командную строку, то a приобретет значение по умолчанию, равное 1. Добавив строку для переменной help, мы также сможем напечатать более эффективное описание для каждой переменной, используя —help:
Простые варианты использования аргументов командной строки
Рассмотрим еще несколько интересных опций с argparse. Во-первых, мы можем указать информацию о типе каждой переменной так, чтобы преобразование типов выполнялось прямо на входе:
Чтобы убедиться, что пользователь всегда передает значение для определенного аргумента можно использовать ключевое слово required. Установленное значение True вынуждает пользователя вводить данные только для этого значения, иначе программа выдаст ошибку и остановится.
Если мы установим значение для аргумента —name, то программа будет работать отлично! Однако при его отсутствии мы получим подобное сообщение об ошибке:
usage: run.py [-h] [—a A] —name NAME
run.py: error: the following arguments are required: —name
Мы также можем ввести возможные значения для определенного аргумента командной строки с помощью аргумента choices. Полезная функция, особенно при наличии в коде набора операторов if-else, которые выполняют определенные операции на основе одной строки. Пример:
Теперь при вводе любого значения, находящегося в списке choices, код будет работать и примет аргумент education. Однако при вводе того, что отсутствует в списке, например, числа 5, вы получите следующее сообщение, в котором предлагается выбрать варианты из списка:
usage: run.py [-h] [—a A] —education
run.py: error: argument —education: invalid choice: ‘5’ (choose from ‘highschool’, ‘college’, ‘university’, ‘other’)
Продвинутые варианты использования и советы
Настало время двигаться дальше и перейти к продвинутому argparse!
В аргументе action можно указать действие, которое должен принимать парсер аргументов. Например, аргумент, который автоматически устанавливается равным булевому значению True, если оно присутствует или является константой. В примере ниже показаны оба случая.
В приведенном выше коде говорится, что если в командной строке присутствует аргумент a, то он будет иметь значение True, а в противном случае False. Аналогичным образом, при наличии аргумента командной строки b он должен иметь значение 10, а в противном случае None из-за отсутствия значения по умолчанию!
Мы также можем установить так называемые взаимоисключающие группы , определяющие набор аргументов командной строки парсера, которые не могут быть переданы одновременно.
Аргументы argparse — a и b — добавлены в одну взаимоисключающую группу. Таким образом, Python не позволит отправлять a и b одновременно, подобно python3 argparse_8.py —a —b. Эта функция гарантирует, что пользователь не передаст противоречащие друг другу переменные одновременно, что позволяет избежать путаницы или ошибок.
Вот и все! Вы прошли простое руководство по аргументам командной строки Python.
Пишем интерфейсы командной строки в Python как профи
Все мы, специалисты по анализу данных, выполняем множество рутинных и повторяющихся действий. Сюда относятся: создание еженедельных отчетов, ETL -операции (извлечение, преобразование, загрузка), обучение моделей с помощью различных наборов данных и т.д. Зачастую на выходе у нас появляется множество Python-скриптов, и каждый раз при выполнении кода нам приходится менять его параметры. Лично меня это бесит! Именно поэтому я стал превращать скрипты в повторно используемые инструменты интерфейса командной строки (CLI-инструменты). Это повысило эффективность и продуктивность моей каждодневной работы. Начинал я с Argparse , но не особо проникся им, поскольку приходилось писать множество убогого кода. И тут я подумал: неужели нельзя достичь тех же результатов без постоянного переписывания кода? Да и вообще, смогу ли я когда-нибудь получать удовольствие от создания CLI-инструментов?
Так что же такое Click? Из официальной документации следует:
Click призван сделать процесс написания инструментов командной строки быстрым и увлекательным, избавляя при этом от всякого рода разочарований из-за невозможности реализации желаемого CLI API.
Звучит шикарно! Как считаете?
В данной статье я поделюсь с вами практическим руководством по пошаговому созданию Python CLI с помощью Click на Python и продемонстрирую вам базовые опции и преимущества этой библиотеки. Выполнив данный пример, вы научитесь писать CLI-инструменты быстро и безболезненно 🙂 Давайте уже займемся делом!
Обучающий урок
В ходе данного урока мы будем пошагово создавать CLI с помощью Click на Python. Я начну с самых основ и в каждом шаге буду рассказывать про концепцию, предлагаемую Click. Дополнительно мне понадобится Poetry для управления пакетами и зависимостями.
Подготовка
Для начала давайте установим Poetry. Существует множество способов установки, однако здесь мы воспользуемся pip:
pip install poetry==0.12.7
Затем создадим в Poetry новый проект и назовем его cli-tutorial . Далее добавим зависимости click и funcy и создадим файл cli.py, который позже заполним кодом.
Я включил сюда funcy, поскольку он пригодится мне в дальнейшем. Ну а сейчас мы готовы к реализации своего первого CLI. Небольшое примечание: пример кода можно найти на GitHub.
Наш первый CLI на Click
Наш первоначальный CLI читает CSV-файл с диска, обрабатывает его (как именно он это делает — пока что не важно) и сохраняет результат в Excel. Пути к входному и выходному файлу настраиваются пользователем. И пользователь должен указать путь к входному файлу. Путь к выходному файлу указывается по желанию. Обычно им считаетсяoutput.xlsx. Вот так выглядит этот код при использовании Click:
И что мы тут делаем?
1. Мы декорируем метод process, который будет вызываться из командной строки черезclick.command .
2. Затем определяем аргументы командной строки через декораторclick.option. Но внимательно следите за правильными названиями аргументов в декорированной функции. Если в click.option добавляется строка без дефиса, то аргумент должен совпадать с этой строкой. Этим и объясняется —in и in_file. Если все имена начинаются с дефисов, то Click создает название аргумента по самому длинному имени и заменяет все дефисы внутри слова на нижнее подчеркивание. Название пишется в нижнем регистре. Пример: —out-file и out_file. Более подробно можно почитать в документации по Click .
3. Через соответствующие аргументы click.option задаем наши предварительные условия значениями по умолчанию или необходимыми аргументами.
4. Добавляем текст справки к нашим аргументами. Он будет показываться при вызове функции через —help. Здесь же отображается docstring из нашей функции.
Теперь можете вызвать этот CLI несколькими способами:
Круто! Вот мы и создали свой первый CLI с помощью Click!
Обратите внимание: я не прописываю read_csv, process_csv и write_excel, т.к. предполагаю, что они существуют и корректно выполняют свою работу.
Одной из проблем CLI является то, что мы передаем параметры как общие строки. Почему же это проблема? Да потому, что такие строки должны быть преобразованы к фактическим типам. А это может приводить к ошибкам из-за плохо отформатированного пользовательского ввода. Взгляните на пример, в котором мы использовали пути и пытались загрузить CSV-файл. Пользователь может указать строку, которая и вовсе не является путем. И даже если эту строку правильно отформатировать, нужный файл может отсутствовать либо же у вас не окажется прав доступа. Разве не правильнее было бы автоматически проверять ввод и интерпретировать его или сразу выдавать ошибку с информативным сообщением? И в идеале все это делалось бы без написания длиннющих кусков кода. Click с нами полностью согласен. Поэтому в нем можно задавать тип аргументов.
Спецификация типов
В нашем примере с CLI мы хотели, чтобы пользователь передавал корректный путь к существующему файлу, для которого у нас есть разрешения на чтение. Если эти условия соблюдены, то мы загружаем входной файл. Кроме того, пользователь может задать путь к выходному файлу, и этот путь также должен быть действительным. Все это можно сделать, передав объект click.Path в аргумент type декоратора click.option.
click.Path — это один из нескольких готовых типов в Click. Помимо стандартных решений, вы можете создавать настраиваемые типы. Однако в данной статье эта тема не освещается. Почитать подробнее про пользовательские типы можно в документации .
Логические флаги
Еще одна полезная функция Click — это логические флаги. И, пожалуй, самым известным из них является флагverbose. При значении true ваш инструмент выводит всю информацию в терминал. При значении false показываются только некоторые данные. В Click это можно реализовать следующим образом:
Все, что от вас требуется, — это добавить еще один декоратор click.option и установить is_flag=True. Теперь для получения подробного вывода нужно всего лишь вызвать CLI:
python -m cli_tutorial.cli -i path/to/some/file.cs —verbose
Переключатель функций
Допустим, нам захотелось не просто хранить результат в локальном process_csv, но и загружать его на сервер. Кроме того, есть не только целевой сервер, но и сервера для разработки, тестирования и реальной базы. И ко всем им нужно обращаться по разным URL. Один из способов выбора сервера — это передача полного URL— адреса как аргумента, который пользователь должен будет прописать. Причем, этот способ не просто рискованный в плане ошибок, но и весьма кропотлив. Поэтому для облегчения жизни пользователей я использую переключатели функций. Принцип их работы лучше всего иллюстрирует код ниже:
Здесь я добавил три декоратораclick.option для трех возможных URL-адресов серверов. Важный момент: все три опции содержат одну общую переменнуюserver_url. В зависимости от выбранной опции значение server_url соответствует значению, определенному в flag_value. Их вы выбираете, добавляя в качестве аргумента —dev , —test или —prod. Таким образом, при выполнении:
python -m cli_tutorial.cli -i path/to/some/file.csv —test
server_url соответствует https://test.server.com/api/v2/upload. Если оставить флажки пустыми, то Click возьмет значение —prod, поскольку я прописал default=True.
Запрос на ввод логина и пароля
К счастью или несчастью, наши сервера защищены паролями. Так что для загрузки файла на сервер потребуется имя пользователя и пароль. Конечно же, их можно задать стандартными аргументами click.option. Но тогда ваш пароль сохранится в виде обычного текста в истории команд, а это несет определенную угрозу для безопасности.
Поэтому мы предпочитаем выдавать пользователю запрос на ввод пароля, не передавая его в терминал и не сохраняя в истории команд. Что до логина, то здесь нам по душе простой запрос на ввод с передачей в терминал. А при определенных познаниях в Click ничего проще и не придумаешь. Вот наш код:
Чтобы добавить подсказку для ввода аргумента, установите prompt=True. Это действие добавит запрос везде, где пользователь не проставил аргумент — user , однако он может потребоваться. Если нажать на Enter в запросе, то проставится значение по умолчанию. Оно определяется с помощью другой полезной функции Click.
Запрос на ввод и подтверждение пароля без отправки его в терминал стало чем-то настолько обыденным, что Click придумал для этого специальный декораторpassword_option. Важное примечание: пользователь все равно будет передавать пароль через —password MYSECRETPASSWORD. Однако так он сможет этого не делать.
Вот и все. Мы создали полноценный CLI. Перед тем, как подвести финальную черту, мне бы хотелось поделиться еще одной полезной подсказкой.
Poetry-скрипты
Последним штрихом этой статьи, который никак не связан с самим Click, но идеально вписывается в тему CLI, является создание Poetry -скриптов . Эти скрипты позволяют создавать исполняемые модули для вызова Python-функций из командной строки так же, как это делается в Setuptools -скриптах . Как это выглядит? Для начала добавим в файл pyproject.toml следующие строки:
[tool.poetry.scripts]your-wanted-name = ‘cli_tutorial.cli:process’
Значение your-wanted-name — это псевдоним для функции process, определенной в модулеcli_tutorial.cli. Теперь вы можете вызвать ее следующим образом:
poetry run your-wanted-name -i ./dummy.csv — verbose — dev
Таким образом, вы, например, сможете включать несколько CLI-функций в один файл, определять псевдонимы и не добавлять блок if __name__ == “__main__”.
В данной статье я показал вам, как пользоваться Click и Poetry для простого и эффективного создания CLI-инструментов. Это был лишь небольшой пример возможностей Click. В библиотеке есть и другие полезные функции, как, например, обратные вызовы , вложенные команды или предварительный выбор значений . Еще раз призываю всех заинтересовавшихся темой почитать документацию по Click.