С того дня, как я начал изучать и работать с Python, argparse был де-факто модулем для написания CLI (Ccommand Line I интерфейс) скрипты. Однако недавно я наткнулся на другой пакет Python, который очень просто помогает в создании CLI-скриптов: Click (Ccommand Line Iинтерфейс Ссоздание Kэто).

О чем не эта статья

Это не сравнение между argparse и click или любым другим модулем, который позволяет реализовать интерфейс командной строки в Python.

О чем эта статья

Мы увидим, как создать простой интерфейс командной строки, используя возможности click.

Пойдем!

Монтаж

Обычное дело. Просто pip install click и все готово.

Как правило, рекомендуется использовать виртуальную среду, чтобы иметь хорошее разделение между различными средами Python, а не загромождать одну среду множеством пакетов, которые могут вам не понадобиться во всех приложениях.

Сначала запустите pip install virtualenv, чтобы установить пакет. Затем создайте новую папку для нашего маленького скрипта и создайте внутри виртуальную среду venv:

$ mkdir clickdemo
$ cd clickdemo
$ virtualenv venv

А теперь просто активируйте новую среду. В Mac OS X или Linux:

$ . venv/bin/activate

В Windows:

$ venv\scripts\activate

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

(venv) $

затем мы можем установить click внутри нашей новой среды, запустив pip install click.

Основной пример

Давайте реализуем скрипт clickdemo.py, который просто говорит «Привет!»

#clickdemo.py

import click


@click.command()
def greet():
    print("Hello!")


if __name__ == '__main__':
    greet()

Теперь запускаем его и проверяем вывод:

(venv) $ python clickdemo.py
Hello!

Очень просто.

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

(venv) $ python clickdemo.py --help
Usage: clickdemo.py [OPTIONS]

Options:
  --help  Show this message and exit.

И обновить справочное сообщение так же просто, просто добавьте строку документации в свою командную функцию:

@click.command()
def greet():
    """Greetings stranger!"""
    print("Hello")

Теперь попробуем еще раз:

(venv) $ python clickdemo.py --help
Usage: clickdemo.py [OPTIONS]

  Greetings stranger!

Options:
  --help  Show this message and exit.

Прохладный! Теперь у нас есть общее представление о том, как создать простой сценарий командной строки. Как насчет добавления некоторых параметров?

Дополнительные параметры

А.к.а.. ну .. Варианты.

Мы хотим, чтобы пользователи по желанию указывали свое имя и возраст.

@click.command()
@click.option('--name', '-n', default='Stranger',
              type=click.STRING, help="Your name", show_default=True)
@click.option('-a', '--age', default=None, type=int, help="Your age")
def greet(name, age):
    """Greetings stranger!"""
    print(f"Hello {name}!")
    if age is not None:
        print(f"You are {age} years old.")

Несколько вещей, на которые следует обратить внимание:

  • Вы можете указать значение по умолчанию для вашего параметра, а также тип
  • Установка флага show_default покажет значение по умолчанию в справочном сообщении.
  • Нам нужно создать новый аргумент Python в украшенной функции для каждого параметра, который мы задаем в командной строке.

Когда дело доходит до именования этих аргументов, следует помнить о некоторых моментах:

  1. Если имя не имеет префикса, оно используется как имя аргумента Python и не обрабатывается как имя параметра в командной строке.
  2. Если имеется хотя бы одно имя с префиксом из двух дефисов, в качестве имени используется первое указанное имя.
  3. В противном случае используется имя с префиксом в один тире.

Ммм хорошо, что это значит? Давайте рассмотрим несколько примеров:

@click.option('-n', '--name') -> Function argument name: `name`
@click.option('-n') -> Function argument name: `n`
@click.option('-n', '--name', 'username') -> Function argument name: `username`
@click.option('--UserName') -> Function argument name: `username`
@click.option('--name', '--alternative') -> Function argument name: `name`
@click.option('-n', '-nm') -> Function argument name: `n`
@click.option('---name') -> Function argument name: `_name`

Мы можем добавить логические флаги с одним значением, установив is_flag=True:

@click.command()
@click.option('--name', '-n', default='Stranger',
              type=click.STRING, help="Your name", show_default=True)
@click.option('-a', '--age', default=None, type=int, help="Your age")
@click.option('-j', 'jedi', is_flag=True, default=False,
              help="Are you a Jedi Master?")
def greet(name, age, jedi):
    """Greetings stranger!"""
    greeting = f"Hello Master {name}!" if jedi else f"Hello {name}!"
    print(greeting)
    if age is not None:
        print(f"You are {age} years old.")

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

@click.command()
@click.option('--name', '-n', default='Stranger',
              type=click.STRING, help="Your name", show_default=True)
@click.option('-a', '--age', default=None, type=int, help="Your age")
@click.option('--jedi/--not-jedi', default=False,
              help="Are you a Jedi Master?")
def greet(name, age, jedi):
    """Greetings stranger!"""
    greeting = f"Hello Master {name}!" if jedi else f"Hello {name}!"
    print(greeting)
    if age is not None:
        print(f"You are {age} years old.")

И вот как мы будем его использовать:

(venv) $ python clickdemo.py -n Anakin --not-jedi
Hello Anakin!

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

Обязательные параметры

А.к.а Аргументы.

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

@click.command()
@click.option('--name', '-n', default='Stranger',
              type=click.STRING, help="Your name", show_default=True)
@click.option('-a', '--age', default=None, type=int, help="Your age")
@click.option('--jedi/--not-jedi', default=False,
              help="Are you a Jedi Master?")
@click.argument('filename', type=click.STRING)
def greet(name, age, jedi, filename):
    """Greetings stranger!"""
    greeting = f"Hello Master {name}!" if jedi else f"Hello {name}!"
    print(greeting)
    if age is not None:
        print(f"You are {age} years old.")
    print(f"The output will be saved to {filename})

Затем аргумент можно указать в командной строке следующим образом:

(venv) $ python clickdemo.py -n Anakin --not-jedi output.txt
Hello Anakin!
Your output will be saved to output.txt

Click обрабатывает для нас чтение и запись в файлы через тип click.File. Нам просто нужно указать, хотим ли мы открыть файл для чтения rили для записи w.

@click.command()
@click.option('--name', '-n', default='Stranger', type=click.STRING, help="Your name", show_default=True)
@click.option('-a', '--age', default=None, type=int, help="Your age")
@click.option('--jedi/--not-jedi', default=False, help="Are you a Jedi Master?")
@click.argument('input', type=click.File('r'))
@click.argument('output', type=click.File('w'))
def greet(name, age, jedi, input, output):
    """Greetings stranger!"""
    greeting = f"Hello Master {name}!" if jedi else f"Hello {name}!"
    print(greeting)
    if age is not None:
        print(f"You are {age} years old.")

    info = input.read()
    output.write(info)

Тестирование в командной строке:

python3 clickdemo.py -n Anakin --not-jedi -- - output.txt
Hello Anakin!
test one line
and another one

Здесь мы принимаем ввод из командной строки, указав - в качестве входного аргумента и записывая его обратно в output.txt. Обратите внимание, что здесь мы определили параметры командной строки по-другому, используя строку -- для разделения списка параметров и списка аргументов.

Подробнее об Аргументах читайте в Документации по аргументам.

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

И это обертка. Спасибо, что дошли до конца.

До следующего раза!

Использованная литература: