Комментарии 7
У argparse есть фишка — сабпарсерам можно задать функцию, которая будет обрабатывать вызов. Благодаря этому, лесенка if-ов в main
if cmd == "add":
task = app.add_task(args.title)
print(task, "created with number", task.number, end=".\n")
elif cmd == "show":
app.print_tasks(args.show_done)
elif cmd == "done":
task = app.task_done(args.number)
print(task, "marked as done.")
elif cmd == "remove":
task = app.remove_task(args.number)
print(task, "removed from list.")
сократится до одной строчки args.func(args)
. Пример из документации:
>>> # sub-command functions
>>> def foo(args):
... print(args.x * args.y)
...
>>> def bar(args):
... print('((%s))' % args.z)
...
>>> # create the top-level parser
>>> parser = argparse.ArgumentParser()
>>> subparsers = parser.add_subparsers()
>>>
>>> # create the parser for the "foo" command
>>> parser_foo = subparsers.add_parser('foo')
>>> parser_foo.add_argument('-x', type=int, default=1)
>>> parser_foo.add_argument('y', type=float)
>>> parser_foo.set_defaults(func=foo)
>>>
>>> # create the parser for the "bar" command
>>> parser_bar = subparsers.add_parser('bar')
>>> parser_bar.add_argument('z')
>>> parser_bar.set_defaults(func=bar)
>>>
>>> # parse the args and call whatever function was selected
>>> args = parser.parse_args('foo 1 -x 2'.split())
>>> args.func(args)
2.0
>>>
>>> # parse the args and call whatever function was selected
>>> args = parser.parse_args('bar XYZYX'.split())
>>> args.func(args)
((XYZYX))
Тут скорее не «задать функцию, которая будет обрабатывать вызов», а творческий подход с как бы добавлением фиктивного параметра (func
в данном случае) с установкой ему значения по умолчанию в нужную нам функцию-обработчик (потому что функция — объект первого класса, так что почему бы ей не быть умолчательным значением для параметра). Но так как реально такой аргумент вызовом add_argument()
мы не добавляли, получается, что параметр func
всегда принимает значение по умолчанию и попадает в словарь разобранных параметров, ну а дальше мы его просто вызываем как функцию. Как по мне, типично Python'ий подход.
Минусы: Какие у «клика» минусы — это сложный вопрос. Может, он чего-то не умеет из того, на что способны следующие библиотеки?
Приходилось плотно работать с click, на память вот какие есть минусы:
- нельзя задать аналог
nargs='+'
для опциональных аргументов. Только позиционные аргументы можно передавать в неопределённом количестве. В какой-то степени это исключает ambiguous поведение при смешивании команд и опций, но на самом деле всё решаемо и это недоработка, которую не будут исправлять. https://github.com/pallets/click/issues/484 - проблема с chain, а именно: нельзя использовать одновременно позиционные и опциональные аргументы в chain-командах. Это неисправимая проблема из-за которой хотят вообще убрать поддержку chain (что сломает много программ, завязанных на эту функциональность) https://github.com/pallets/click/issues/1269
- встроенный help криво отображает позиционные аргументы (об этом есть в документации)
- встроенный help никак явно не отображает, что опциональный аргумент является multiple, если он таковой (
multiple=True
) - Нет поддержки mutually exclusive групп и не будет: https://github.com/pallets/click/issues/257
Это то, что вспомнил, есть ещё разные подводные камни, на которые натыкаешься если используешь click на полную мощность. В остальном это лучшая библиотека для создания cli на мой взгляд.
Сравнение популярных CLI-библиотек для Python: click, cement, fire и другие