Аргументы
Аргументы работают аналогично опциям, но являются позиционными. Также они поддерживают только подмножество функций опций из-за своего синтаксического характера. Click также не будет пытаться документировать для вас аргументы и хочет, чтобы вы документировали их вручную, чтобы избежать уродливых страниц справки.
Базовые аргументы
Наиболее базовая опция — это простой строковый аргумент одного значения. Если тип не указан, используется тип значения по умолчанию, а если значение по умолчанию не указано, тип предполагается как STRING.
Пример:
@click.command()
@click.argument('filename')
def touch(filename):
"""Print FILENAME."""
click.echo(filename)
И как это выглядит:
$ touch foo.txt foo.txt
Аргументы с переменным числом значений
Вторая наиболее распространённая версия — это аргументы с переменным числом значений, где принимается определённое (или неограниченное) количество аргументов. Это можно контролировать с помощью параметра nargs. Если он установлен на значение -1, то принимается неограниченное количество аргументов.
Значение затем передаётся как кортеж. Обратите внимание, что только один аргумент может быть установлен на nargs=-1, так как он съест все аргументы.
Пример:
@click.command()
@click.argument('src', nargs=-1)
@click.argument('dst', nargs=1)
def copy(src, dst):
"""Move file SRC to DST."""
for fn in src:
click.echo(f"move {fn} to folder {dst}")
И как это выглядит:
$ copy foo.txt bar.txt my_folder move foo.txt to folder my_folder move bar.txt to folder my_folder
Обратите внимание, что так вы бы не писали это приложение. Причина в том, что в этом конкретном примере аргументы определены как строки. Однако имена файлов не являются строками! Они могут быть на некоторых операционных системах, но не обязательно на всех. Для лучших способов написания этого см. следующие разделы.
Примечание об аргументах с переменным числом значений, не являющихся пустыми
Если вы пришли из argparse, вам может не хватать поддержки установки nargs в значение +, чтобы указать, что требуется как минимум один аргумент.
Это поддерживается установкой required=True. Однако этого следует избегать, так как мы считаем, что скрипты должны корректно деградировать до noops, если аргумент с переменным числом значений пустой. Причина в том, что очень часто скрипты вызываются с символами подстановки из командной строки, и они не должны завершаться ошибкой, если символы подстановки пусты.
Аргументы файлов
Поскольку все примеры уже работали с именами файлов, имеет смысл объяснить, как правильно обращаться с файлами. Инструменты командной строки более интересны, если они работают с файлами по-unix-овски, то есть принимают - в качестве специального файла, который ссылается на stdin/stdout.
Click поддерживает это через тип click.File, который интеллектуально обрабатывает файлы за вас. Он также правильно обрабатывает Unicode и байты для всех версий Python, поэтому ваш скрипт остаётся очень переносимым.
Пример:
@click.command()
@click.argument('input', type=click.File('rb'))
@click.argument('output', type=click.File('wb'))
def inout(input, output):
"""Copy contents of INPUT to OUTPUT."""
while True:
chunk = input.read(1024)
if not chunk:
break
output.write(chunk)
И что он делает:
$ inout - hello.txt hello ^D $ inout hello.txt - hello
Аргументы путей к файлам
В предыдущем примере файлы открывались немедленно. Но что, если нам нужно только имя файла? Примитивный способ — использовать тип аргумента строки по умолчанию. Тип Path имеет несколько проверок, которые выдают приятные ошибки, если они терпят неудачу, например, проверка существования. Имена файлов в этих сообщениях об ошибках отформатированы с помощью format_filename(), поэтому любые нераспознаваемые байты будут напечатаны красиво.
Пример:
@click.command()
@click.argument('filename', type=click.Path(exists=True))
def touch(filename):
"""Print FILENAME if the file exists."""
click.echo(click.format_filename(filename))
И что он делает:
$ touch hello.txt hello.txt $ touch missing.txt Usage: touch [OPTIONS] FILENAME Try 'touch --help' for help. Error: Invalid value for 'FILENAME': Path 'missing.txt' does not exist.
Безопасность открытия файлов
Тип FileType имеет одну проблему, с которой ему нужно справиться, и это решение, когда открывать файл. По умолчанию используется «интеллектуальное» поведение. Это означает, что он будет открывать stdin/stdout и файлы, открытые для чтения, немедленно. Это даст пользователю прямую обратную связь, когда файл не может быть открыт, но он будет открывать файлы для записи только в первый раз, когда выполняется операция ввода-вывода, автоматически обернув файл в специальную оболочку.
Это поведение можно принудительно изменить, передав lazy=True или lazy=False в конструктор. Если файл открывается лениво, он потерпит неудачу в своей первой операции ввода-вывода, выбросив FileError.
Поскольку файлы, открытые для записи, обычно немедленно опустошают файл, режим ленивого открытия следует отключать только в том случае, если разработчик абсолютно уверен, что это именно то поведение, которое нужно.
Принудительное включение ленивого режима также очень полезно для предотвращения путаницы с обработкой ресурсов. Если файл открыт в ленивом режиме, он получит метод close_intelligently, который может помочь определить, нужно ли закрывать файл или нет. Это не нужно для параметров, но необходимо для ручного запроса с помощью функции prompt(), так как вы не знаете, был ли поток, такой как stdout, открыт (который был открыт ранее) или реальный файл, который нужно закрыть.
Начиная с Click 2.0, также можно открывать файлы в атомарном режиме, передав atomic=True. В атомарном режиме все записи выполняются в отдельный файл в той же папке, а по завершении файл будет перемещён в исходное расположение. Это полезно, если файл, который регулярно читается другими пользователями, изменяется.
Переменные окружения
Как и опции, аргументы также могут получать значения из переменной окружения. В отличие от опций, однако, это поддерживается только для явно указанных переменных окружения.
Пример использования:
@click.command()
@click.argument('src', envvar='SRC', type=click.File('r'))
def echo(src):
"""Print value of SRC environment variable."""
click.echo(src.read())
И из командной строки:
$ export SRC=hello.txt $ echo Hello World!
В этом случае это также может быть список разных переменных окружения, где выбирается первая.
В целом, эта функция не рекомендуется, так как может вызвать много путаницы у пользователя.
Аргументы, похожие на опции
Иногда вы хотите обработать аргументы, которые выглядят как опции. Например, представьте, что у вас есть файл с именем -foo.txt. Если вы передадите его как аргумент таким образом, Click будет обрабатывать его как опцию.
Чтобы решить эту проблему, Click делает то же, что и любой скрипт командной строки в стиле POSIX, то есть принимает строку -- в качестве разделителя опций и аргументов. После маркера --, все последующие параметры принимаются как аргументы.
Пример использования:
@click.command()
@click.argument('files', nargs=-1, type=click.Path())
def touch(files):
"""Print all FILES file names."""
for filename in files:
click.echo(filename)
И из командной строки:
$ touch -- -foo.txt bar.txt -foo.txt bar.txt
Если вам не нравится маркер --, вы можете установить ignore_unknown_options в значение True, чтобы избежать проверки неизвестных опций:
@click.command(context_settings={"ignore_unknown_options": True})
@click.argument('files', nargs=-1, type=click.Path())
def touch(files):
"""Print all FILES file names."""
for filename in files:
click.echo(filename)
И из командной строки:
$ touch -foo.txt bar.txt -foo.txt bar.txt
© 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/arguments/