Ads
Comments 7
Тоже недавно выбирали CLI. Остановились на Click. Смотрели на Fire. Он подойдет когда написал приложение, а тебе, вдруг, сказали сделать для него CLI по быстроляну :)

У 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 на мой взгляд.

Only those users with full accounts are able to leave comments. Log in, please.