Spec-Zone.ru › click

Параметры

Добавление параметров к командам можно выполнить с помощью декоратора option(). Поскольку параметры могут быть различных типов, есть множество параметров для настройки их поведения. Параметры в Click отличаются от позиционных аргументов.

Наименование параметров

Параметры имеют имя, которое будет использоваться в качестве имени Python-аргумента при вызове декорированной функции. Это имя может быть выведено из имени параметра или задано явно. Имена указываются в качестве позиционных аргументов декоратору.

Имя выбирается в следующем порядке:

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

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

@click.command()
@click.option('-s', '--string-to-echo')
def echo(string_to_echo):
    click.echo(string_to_echo)
@click.command()
@click.option('-s', '--string-to-echo', 'string')
def echo(string):
    click.echo(string)
  • "-f", "--foo-bar", имя равно foo_bar
  • "-x", имя равно x
  • "-f", "--filename", "dest", имя равно dest
  • "--CamelCase", имя равно camelcase
  • "-f", "-fb", имя равно f
  • "--f", "--foo-bar", имя равно f
  • "---f", имя равно _f

Основные параметры значений

Наиболее базовый параметр — это параметр значения. Эти параметры принимают одно значение. Если тип не указан, используется тип значения по умолчанию. Если значение по умолчанию не указано, предполагается тип STRING. Если имя не задано явно, то используется имя первого длинного параметра, иначе первого короткого. По умолчанию параметры не обязательны, однако, чтобы сделать параметр обязательным, просто передайте required=True в качестве аргумента декоратору.

@click.command()
@click.option('--n', default=1)
def dots(n):
    click.echo('.' * n)
# How to make an option required
@click.command()
@click.option('--n', required=True, type=int)
def dots(n):
    click.echo('.' * n)
# How to use a Python reserved word such as `from` as a parameter
@click.command()
@click.option('--from', '-f', 'from_')
@click.option('--to', '-t')
def reserved_param_name(from_, to):
    click.echo(f"from {from_} to {to}")

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

$ dots --n=2
..

В этом случае параметр имеет тип INT, потому что значение по умолчанию — целое число.

Чтобы отобразить значения по умолчанию при отображении справки по команде, используйте show_default=True

@click.command()
@click.option('--n', default=1, show_default=True)
def dots(n):
    click.echo('.' * n)
$ dots --help
Usage: dots [OPTIONS]

Options:
  --n INTEGER  [default: 1]
  --help       Show this message and exit.

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

@click.command()
@click.option('--n', default=1, show_default=True)
@click.option("--gr", is_flag=True, show_default=True, default=False, help="Greet the world.")
@click.option("--br", is_flag=True, show_default=True, default=True, help="Add a thematic break")
def dots(n, gr, br):
    if gr:
        click.echo('Hello world!')
    click.echo('.' * n)
    if br:
        click.echo('-' * n)
$ dots --help
Usage: dots [OPTIONS]

Options:
  --n INTEGER  [default: 1]
  --gr         Greet the world.
  --br         Add a thematic break  [default: True]
  --help       Show this message and exit.

Параметры с несколькими значениями

Иногда у вас есть параметры, которые принимают более одного аргумента. Для параметров поддерживается только фиксированное количество аргументов. Это можно настроить параметром nargs. Значения хранятся в виде кортежа.

@click.command()
@click.option('--pos', nargs=2, type=float)
def findme(pos):
    a, b = pos
    click.echo(f"{a} / {b}")

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

$ findme --pos 2.0 3.0
2.0 / 3.0

Кортежи как параметры с несколькими значениями

Changelog

Новое в версии 4.0.

Как видите, используя nargs, устанавливаемое в определённое число, каждый элемент в результирующем кортеже имеет одинаковый тип. Это может быть не то, что вам нужно. Часто вы можете использовать разные типы для разных индексов кортежа. Для этого вы можете напрямую указать кортеж как тип:

@click.command()
@click.option('--item', type=(str, int))
def putitem(item):
    name, id = item
    click.echo(f"name={name} id={id}")

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

$ putitem --item peter 1338
name=peter id=1338

Используя кортеж как тип, nargs автоматически устанавливается в длину кортежа, и тип click.Tuple используется автоматически. Приведённый выше пример эквивалентен этому:

@click.command()
@click.option('--item', nargs=2, type=click.Tuple([str, int]))
def putitem(item):
    name, id = item
    click.echo(f"name={name} id={id}")

Несколько параметров

Аналогично nargs, также есть случай, когда требуется поддержка параметра, передаваемого несколько раз, и все значения записываются — а не только последнее. Например, git commit -m foo -m bar записало бы две строки для сообщения об изменениях: foo и bar. Это можно сделать с помощью флага multiple:

Пример:

@click.command()
@click.option('--message', '-m', multiple=True)
def commit(message):
    click.echo('\n'.join(message))

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

$ commit -m foo -m bar
foo
bar

При передаче default с multiple=True, значение по умолчанию должно быть списком или кортежем, иначе оно будет интерпретировано как список отдельных символов.

@click.option("--format", multiple=True, default=["json"])

Подсчёт

В очень редких случаях интересно использовать повторение параметров для увеличения целого числа. Например, это можно использовать для флагов подробности:

@click.command()
@click.option('-v', '--verbose', count=True)
def log(verbose):
    click.echo(f"Verbosity: {verbose}")

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

$ log -vvv
Verbosity: 3

Логические флаги

Логические флаги — это параметры, которые могут быть включены или выключены. Это можно сделать, определив два флага одновременно, разделенные слэшем (/) для включения или выключения параметра. (Если в строке параметра есть слэш, Click автоматически понимает, что это логический флаг, и неявно передаст is_flag=True.) Click всегда хочет, чтобы вы предоставляли флаги включения и выключения, чтобы вы могли изменить значение по умолчанию позже.

Пример:

import sys

@click.command()
@click.option('--shout/--no-shout', default=False)
def info(shout):
    rv = sys.platform
    if shout:
        rv = rv.upper() + '!!!!111'
    click.echo(rv)

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

$ info --shout
LINUX!!!!111
$ info --no-shout
linux
$ info
linux

Если вам действительно не нужен выключатель, вы можете определить один и вручную сообщить Click, что это флаг:

import sys

@click.command()
@click.option('--shout', is_flag=True)
def info(shout):
    rv = sys.platform
    if shout:
        rv = rv.upper() + '!!!!111'
    click.echo(rv)

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

$ info --shout
LINUX!!!!111
$ info
linux

Обратите внимание, что если в вашем параметре уже есть слэш (например, если вы используете параметры в стиле Windows, где / — символ префикса), вы можете альтернативно разделить параметры через ;:

@click.command()
@click.option('/debug;/no-debug')
def log(debug):
    click.echo(f"debug={debug}")

if __name__ == '__main__':
    log()
Changelog

Изменено в версии 6.0.

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

Пример:

import sys

@click.command()
@click.option('--shout/--no-shout', ' /-S', default=False)
def info(shout):
    rv = sys.platform
    if shout:
        rv = rv.upper() + '!!!!111'
    click.echo(rv)
$ info --help
Usage: info [OPTIONS]

Options:
  --shout / -S, --no-shout
  --help                    Show this message and exit.

Переключатели функций

Помимо логических флагов, также есть переключатели функций. Они реализуются путём задания нескольких параметров с одинаковым именем параметра и определением значения флага. Обратите внимание, что путём предоставления параметра flag_value, Click неявно установит is_flag=True.

Чтобы установить флаг по умолчанию, присвойте значение True флагу, который должен быть по умолчанию.

import sys

@click.command()
@click.option('--upper', 'transformation', flag_value='upper',
              default=True)
@click.option('--lower', 'transformation', flag_value='lower')
def info(transformation):
    click.echo(getattr(sys.platform, transformation)())

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

$ info --upper
LINUX
$ info --lower
linux
$ info
LINUX

Параметры выбора

Иногда вам нужно, чтобы параметр был выбором из списка значений. В этом случае можно использовать тип Choice. Его можно создать со списком допустимых значений. Изначально переданный выбор будет возвращён, а не строка, переданная в командной строке. Функции нормализации токенов и case_sensitive=False могут вызвать различие между ними, но они всё ещё будут соответствовать.

Пример:

@click.command()
@click.option('--hash-type',
              type=click.Choice(['MD5', 'SHA1'], case_sensitive=False))
def digest(hash_type):
    click.echo(hash_type)

Как это выглядит:

$ digest --hash-type=MD5
MD5

$ digest --hash-type=md5
MD5

$ digest --hash-type=foo
Usage: digest [OPTIONS]
Try 'digest --help' for help.

Error: Invalid value for '--hash-type': 'foo' is not one of 'MD5', 'SHA1'.

$ digest --help
Usage: digest [OPTIONS]

Options:
  --hash-type [MD5|SHA1]
  --help                  Show this message and exit.

Передавайте только списки или кортежи в качестве вариантов. Другие итерируемые объекты (например, генераторы) могут привести к непредвиденным результатам.

Варианты работают с параметрами, имеющими multiple=True. Если значение default задаётся с multiple=True, оно должно быть списком или кортежем допустимых вариантов.

Варианты должны быть уникальными после учёта влияния case_sensitive и любой указанной функции нормализации токенов.

Changelog

Изменено в версии 7.1: Результирующее значение параметра всегда будет одним из изначально переданных вариантов, независимо от case_sensitive.

Запрос ввода

В некоторых случаях вам нужны параметры, которые можно ввести в командной строке, но если они не введены, вместо этого нужно запросить ввод у пользователя. Это можно реализовать в Click, определив строку запроса.

Пример:

@click.command()
@click.option('--name', prompt=True)
def hello(name):
    click.echo(f"Hello {name}!")

Как это выглядит:

$ hello --name=John
Hello John!
$ hello
Name: John
Hello John!

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

@click.command()
@click.option('--name', prompt='Your name please')
def hello(name):
    click.echo(f"Hello {name}!")

Как это выглядит:

$ hello
Your name please: John
Hello John!

Не рекомендуется использовать запрос вместе с флагом «многократно», вместо этого запрашивайте ввод в функции интерактивно.

По умолчанию пользователь будет запрошен на ввод, если он не был передан через командную строку. Чтобы отключить это поведение, см. Необязательное значение.

Ввод пароля

Click также поддерживает скрытые запросы и запросы подтверждения. Это полезно для ввода пароля:

import codecs

@click.command()
@click.option(
    "--password", prompt=True, hide_input=True,
    confirmation_prompt=True
)
def encode(password):
    click.echo(f"encoded: {codecs.encode(password, 'rot13')}")
$ encode
Password: 
Repeat for confirmation: 
encoded: frperg

Поскольку эта комбинация параметров довольно распространена, её можно заменить декоратором password_option():

@click.command()
@click.password_option()
def encrypt(password):
    click.echo(f"encoded: to {codecs.encode(password, 'rot13')}")

Динамические значения по умолчанию для запросов

Параметры auto_envvar_prefix и default_map для контекста позволяют программе считывать значения параметров из среды или файла конфигурации. Однако это переопределяет механизм запроса, поэтому пользователь не может интерактивно изменить значение.

Если вы хотите позволить пользователю настроить значение по умолчанию, но при этом получить запрос, если параметр не указан в командной строке, вы можете сделать это, предоставив вызываемый объект как значение по умолчанию. Например, чтобы получить значение по умолчанию из среды:

import os

@click.command()
@click.option(
    "--username", prompt=True,
    default=lambda: os.environ.get("USER", "")
)
def hello(username):
    click.echo(f"Hello, {username}!")

Чтобы описать, каким будет значение по умолчанию, установите его в show_default.

import os

@click.command()
@click.option(
    "--username", prompt=True,
    default=lambda: os.environ.get("USER", ""),
    show_default="current user"
)
def hello(username):
    click.echo(f"Hello, {username}!")
$ hello --help
Usage: hello [OPTIONS]

Options:
  --username TEXT  [default: (current user)]
  --help           Show this message and exit.

Обратные вызовы и параметры с немедленным выполнением

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

Примечание: фактическая реализация параметра --version, который можно повторно использовать, доступна в Click как click.version_option(). Приведенный здесь код — всего лишь пример того, как реализовать такой флаг.

В таких случаях вам нужны два понятия: параметры с немедленным выполнением и обратный вызов. Параметр с немедленным выполнением — это параметр, который обрабатывается до других, а обратный вызов — это то, что выполняется после обработки параметра. Немедленное выполнение необходимо, чтобы более ранний необходимый параметр не выводил сообщение об ошибке. Например, если --version не был параметром с немедленным выполнением, а параметр --foo был необходим и определен ранее, вам нужно было бы указать его для работы --version. Для получения дополнительной информации см. Порядок вычисления обратных вызовов.

Обратный вызов — это функция, которая вызывается с тремя параметрами: текущим Context, текущим Parameter и значением. Контекст предоставляет некоторые полезные функции, такие как завершение приложения, и предоставляет доступ к другим уже обработанным параметрам.

Вот пример флага --version:

def print_version(ctx, param, value):
    if not value or ctx.resilient_parsing:
        return
    click.echo('Version 1.0')
    ctx.exit()

@click.command()
@click.option('--version', is_flag=True, callback=print_version,
              expose_value=False, is_eager=True)
def hello():
    click.echo('Hello World!')

Параметр expose_value предотвращает передачу совершенно бесполезного параметра version в обратный вызов. Если бы это не было указано, булево значение передавалось бы скрипту hello. Флаг resilient_parsing применяется к контексту, если Click хочет проанализировать командную строку без каких-либо разрушительных действий, которые могли бы изменить поток выполнения. В данном случае, так как мы завершаем программу, мы ничего не делаем.

Как это выглядит:

$ hello
Hello World!
$ hello --version
Version 1.0

Изменения сигнатуры обратного вызова

В Click 2.0 изменилась сигнатура обратных вызовов. Более подробную информацию об этих изменениях см. в Обновление до 2.0.

Параметры «Да»

Для опасных операций очень полезно иметь возможность запросить у пользователя подтверждение. Это можно сделать, добавив булев флаг --yes и запросив подтверждение, если пользователь его не предоставил, и завершить выполнение в обратном вызове:

def abort_if_false(ctx, param, value):
    if not value:
        ctx.abort()

@click.command()
@click.option('--yes', is_flag=True, callback=abort_if_false,
              expose_value=False,
              prompt='Are you sure you want to drop the db?')
def dropdb():
    click.echo('Dropped all tables!')

И как это выглядит в командной строке:

$ dropdb
Are you sure you want to drop the db? [y/N]: n
Aborted!
$ dropdb --yes
Dropped all tables!

Поскольку это сочетание параметров довольно распространено, его также можно заменить декоратором confirmation_option():

@click.command()
@click.confirmation_option(prompt='Are you sure you want to drop the db?')
def dropdb():
    click.echo('Dropped all tables!')

Изменения сигнатуры обратного вызова

В Click 2.0 изменилась сигнатура обратных вызовов. Более подробную информацию об этих изменениях см. в Обновление до 2.0.

Значения из переменных среды

Очень полезной функцией Click является возможность принимать параметры из переменных среды помимо обычных параметров. Это позволяет гораздо проще автоматизировать инструменты. Например, вы можете передать конфигурационный файл с параметром --config, но также поддерживать экспорт пары ключ-значение TOOL_CONFIG=hello.cfg для более удобного процесса разработки.

Click поддерживает это двумя способами. Один из них — автоматическое создание переменных среды, которое поддерживается только для опций. Чтобы включить эту функцию, параметр auto_envvar_prefix должен быть передан в вызываемый скрипт. Затем каждая команда и параметр добавляются в качестве переменной с верхним регистром и разделителем «_». Если у вас есть подкоманда под названием run, принимающая опцию под названием reload, и префикс — WEB, то переменная — WEB_RUN_RELOAD.

Пример использования:

@click.command()
@click.option('--username')
def greet(username):
    click.echo(f'Hello {username}!')

if __name__ == '__main__':
    greet(auto_envvar_prefix='GREETER')

И из командной строки:

$ export GREETER_USERNAME=john
$ greet
Hello john!

При использовании auto_envvar_prefix с группами команд имя команды должно быть включено в переменной среды между префиксом и именем параметра, т.е. PREFIX_COMMAND_VARIABLE. Если у вас есть подкоманда под названием run-server, принимающая опцию под названием host, и префикс — WEB, то переменная — WEB_RUN_SERVER_HOST.

Пример:

@click.group()
@click.option('--debug/--no-debug')
def cli(debug):
    click.echo(f"Debug mode is {'on' if debug else 'off'}")

@cli.command()
@click.option('--username')
def greet(username):
    click.echo(f"Hello {username}!")

if __name__ == '__main__':
    cli(auto_envvar_prefix='GREETER')
$ export GREETER_DEBUG=false
$ export GREETER_GREET_USERNAME=John
$ cli greet
Debug mode is off
Hello John!

Второй вариант — вручную извлекать значения из определенных переменных среды, определив имя переменной среды в опции.

Пример использования:

@click.command()
@click.option('--username', envvar='USERNAME')
def greet(username):
   click.echo(f"Hello {username}!")

if __name__ == '__main__':
    greet()

И из командной строки:

$ export USERNAME=john
$ greet
Hello john!

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

Множественные значения из значений переменных среды

Поскольку опции могут принимать несколько значений, извлечение таких значений из переменных среды (которые являются строками) немного сложнее. Способ, которым Click решает эту проблему, заключается в том, чтобы предоставить возможность типу настроить это поведение. Для multiple и nargs со значениями, отличными от 1, Click вызовет метод ParamType.split_envvar_value() для выполнения разделения.

По умолчанию для всех типов используется разделение по пробелам. Исключение составляют типы File и Path, которые оба разделяют значения в соответствии с правилами разделения путей операционной системы. В системах Unix, таких как Linux и OS X, разделение происходит по всем двоеточиям (:), а в Windows — по всем точкам с запятой (;).

Пример использования:

@click.command()
@click.option('paths', '--path', envvar='PATHS', multiple=True,
              type=click.Path())
def perform(paths):
    for path in paths:
        click.echo(path)

if __name__ == '__main__':
    perform()

И из командной строки:

$ export PATHS=./foo/bar:./test
$ perform
./foo/bar
./test

Другие символы префикса

Click может обрабатывать альтернативные символы префикса для опций, отличные от -. Это, например, полезно, если вы хотите обрабатывать косые черты как параметры / или что-то подобное. Обратите внимание, что это категорически не рекомендуется в целом, потому что Click хочет, чтобы разработчики придерживались POSIX-семантики. Однако в некоторых ситуациях это может быть полезно:

@click.command()
@click.option('+w/-w')
def chmod(w):
    click.echo(f"writable={w}")

if __name__ == '__main__':
    chmod()

И из командной строки:

$ chmod +w
writable=True
$ chmod -w
writable=False

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

@click.command()
@click.option('/debug;/no-debug')
def log(debug):
    click.echo(f"debug={debug}")

if __name__ == '__main__':
    log()

Диапазоны опций

Тип IntRange расширяет тип INT, чтобы гарантировать, что значение находится в заданном диапазоне. Тип FloatRange делает то же самое для типа FLOAT.

Если min или max опущены, эта сторона не ограничена. Любое значение в этом направлении принимается. По умолчанию обе границы закрыты, что означает, что граничное значение включено в принимаемый диапазон. min_open и max_open могут быть использованы для исключения этой границы из диапазона.

Если режим clamp включен, значение, которое выходит за пределы диапазона, устанавливается на границу вместо завершения с ошибкой. Например, диапазон 0, 5 вернет 5 для значения 10, или 0 для значения -1. При использовании FloatRange режим clamp может быть включен только в том случае, если обе границы закрыты (по умолчанию).

@click.command()
@click.option("--count", type=click.IntRange(0, 20, clamp=True))
@click.option("--digit", type=click.IntRange(0, 9))
def repeat(count, digit):
    click.echo(str(digit) * count)
$ repeat --count=100 --digit=5
55555555555555555555
$ repeat --count=6 --digit=12
Usage: repeat [OPTIONS]
Try 'repeat --help' for help.

Error: Invalid value for '--digit': 12 is not in the range 0<=x<=9.

Обратные вызовы для проверки

Журнал изменений

Изменено в версии 2.0.

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

В Click 1.0 вы можете только генерировать ошибку UsageError, но начиная с Click 2.0, вы также можете генерировать ошибку BadParameter, которая имеет дополнительное преимущество — она автоматически форматирует сообщение об ошибке так, чтобы оно также содержало имя параметра.

def validate_rolls(ctx, param, value):
    if isinstance(value, tuple):
        return value

    try:
        rolls, _, dice = value.partition("d")
        return int(dice), int(rolls)
    except ValueError:
        raise click.BadParameter("format must be 'NdM'")

@click.command()
@click.option(
    "--rolls", type=click.UNPROCESSED, callback=validate_rolls,
    default="1d6", prompt=True,
)
def roll(rolls):
    sides, times = rolls
    click.echo(f"Rolling a {sides}-sided dice {times} time(s)")
$ roll --rolls=42
Usage: roll [OPTIONS]
Try 'roll --help' for help.

Error: Invalid value for '--rolls': format must be 'NdM'

$ roll --rolls=2d12
Rolling a 12-sided dice 2 time(s)

$ roll
Rolls [1d6]: 42
Error: format must be 'NdM'
Rolls [1d6]: 2d12
Rolling a 12-sided dice 2 time(s)

Необязательное значение

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

Установка is_flag=False, flag_value=value указывает Click, что опция всё ещё может получить значение, но если задан только флаг, используется flag_value.

@click.command()
@click.option("--name", is_flag=False, flag_value="Flag", default="Default")
def hello(name):
    click.echo(f"Hello, {name}!")
$ hello
Hello, Default!
$ hello --name Value
Hello, Value!
$ hello --name
Hello, Flag!

Если у опции включено prompt, то установка prompt_required=False указывает Click на отображение запроса только если задан флаг опции, а не если опция вообще не задана.

@click.command()
@click.option('--name', prompt=True, prompt_required=False, default="Default")
def hello(name):
    click.echo(f"Hello {name}!")
$ hello
Hello Default!
$ hello --name Value
Hello Value!
$ hello --name
Name [Default]: 

Если required=True, то опция по-прежнему будет запрашивать значение, если она не указана, но также будет запрашивать значение, если задан только флаг.

© Copyright 2014 Pallets.
Licensed under the BSD 3-Clause License.
We are not supported nor endorsed by Pallets.
https://click.palletsprojects.com/en/8.1.x/options/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API