У меня есть эта строка кода, которая, как ожидается, захватит все имена файлов, переданные моему скрипту Python:
@click.argument("logs", nargs=-1, type=click.File('r'), required=1)
Когда имена файлов не передаются, я хочу по умолчанию использовать -
, то есть стандартный ввод. Итак, если я попытаюсь:
@click.argument("logs", nargs=-1, type=click.File('r'), required=1, default = "-")
click становится несчастным и выдает эту ошибку:
TypeError: nargs=-1 in combination with a default value is not supported.
Есть ли обходной путь для этого? Я попытался установить nargs=0
, но выдает другую ошибку:
IndexError: tuple index out of range
Следующий код:
import click
@click.command()
@click.option('--logs', '-l', multiple=True, default='-')
def cli(logs):
click.echo('\n'.join(logs))
Произведет следующее:
$ myhello -l foo.log -l bar.log -l baz.log
foo.log
bar.log
baz.log
$ myhello
-
Поэтому, если бы вы просто могли называть свою программу по-другому с повторяющимися параметрами, это было бы обходным путем, который вы ищете.
Это меняет семантику того, что хотел OP, так что да, это будет просто обходной путь, который не решает реальную проблему.
в этом случае я бы просто добавил простую обертку вокруг вышеперечисленного в bash.
Поскольку click
прямо заявил, что они отключили эту конкретную функцию, необходимую для вопроса, как сообщается на Эта проблема в их проекте, потребуется обходной путь, который можно легко реализовать как часть функции Python (вместо еще одной кучи кода bash как было предложено в комментарии к другому ответу).
Обходной путь — просто удалить требуемый аргумент и обработать отсутствующие журналы с помощью оператора (аналогично тому, что сделал этот коммит, который ссылался на связанную проблему):
import sys
import click
@click.command()
@click.argument("logs", nargs=-1, type=click.File('r'))
def main(logs):
if not logs:
logs = (sys.stdin,)
print(logs)
# do stuff with logs
if __name__ == '__main__':
main()
Пример исполнения
$ python fail.py
(<_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>,)
$ python fail.py -
(<_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>,)
$ python fail.py foo bar
(<_io.TextIOWrapper name='foo' mode='r' encoding='UTF-8'>, <_io.TextIOWrapper name='bar' mode='r' encoding='UTF-8'>)
Чтобы по умолчанию использовать stdin
для потенциально пустого списка файлов, вы можете определить собственный класс аргументов, например:
class FilesDefaultToStdin(click.Argument):
def __init__(self, *args, **kwargs):
kwargs['nargs'] = -1
kwargs['type'] = click.File('r')
super().__init__(*args, **kwargs)
def full_process_value(self, ctx, value):
return super().process_value(ctx, value or ('-', ))
Определение этого поведения как класса обеспечивает простоту повторного использования.
@click.command()
@click.argument("logs", cls=FilesDefaultToStdin)
def main(logs):
...
Это работает, потому что click — это хорошо спроектированная объектно-ориентированная среда. Декоратор @click.argument()
обычно создает экземпляр объекта click.Argument
, но позволяет переопределить это поведение с помощью параметра cls
. Таким образом, относительно легко наследоваться от click.Argument
в нашем собственном классе и переопределять нужные методы.
В этом случае мы переопределяем click.Argument.full_process_value()
. В нашем full_process_value()
мы ищем пустой список аргументов, и если он пуст, мы добавляем в список аргумент -
(stdin).
Кроме того, мы автоматически присваиваем аргументы nargs=-1
и type=click.File('r')
.
Что бы это ни стоило, соответствующий проблема на гитхабе (т.е. они не исправили это, они явно запретили это).