Для приложения я хочу создать опцию Choice-like, которая должна быть единственной опцией выбора для изменения поведения моего скрипта.Пример: я хочу, чтобы пользователь выбрал с помощью табуляции завершение файла в текущей папке, которым скрипт каким-либо образом будет манипулировать.
Варианты наиболее близкие, которые я мог найти в документации, но я хочу опустить, чтобыПользователь должен знать имя параметра, например --file.Я придумал реализацию на основе команд / групп.Вот минимальный рабочий пример:
# -*- coding: utf-8 -*-
"""Minimal working example for dynamic commands used as choices."""
import click
# A typical click command group without any other arguments or options
@click.group(name='minimal', help=__doc__)
def main():
"""Script's main entry point."""
pass # The following implementation makes the main method somewhat useless
def _convert_allowed_values_to_commands(
group_function, allowed_values, callback):
"""Take each allowed value and create a command under the main group."""
[
_create_command_for_value(group_function, allowed_value, callback)
for allowed_value in allowed_values
]
def _create_command_for_value(group_function, allowed_value, callback):
"""This is the pattern currently used to add commands on the fly."""
def invoke_callback():
callback(allowed_value)
invoke_callback.__name__ = allowed_value
group_function.command(allowed_value)(invoke_callback)
def _callback_function(selected_command):
"""A callback that will do something with the selected command."""
click.echo('do_something(' + selected_command + ')')
# List will later be generated somehow specific for the actual use case
allowed_input_values = ['foo', 'bar', 'foobar']
# Take the input values and create a command each under the main group
_convert_allowed_values_to_commands(
main, allowed_input_values, _callback_function)
# if __main__ pattern only used for demonstration. Later setuptools is used
if __name__ == "__main__":
main()
Он решает, что пользователь может завершить вкладку в более сложном CLI с подкомандами для этой конкретной команды, и ему будут предложены все варианты на другой вкладке.Но он чувствует себя очень плохо и не очень соответствует клику и имеет проблему с тем, что делает функцию main () более или менее бесполезной и, вероятно, не будет хорошо интегрироваться с другими опциями / аргументами.к документации или примерам, как сделать это ближе к типичным интерфейсам / декораторам клика?
Заранее спасибо!
Редактировать:
Для чего стоит, как только я записал все, я нашел другой вариант, используя click.MultiCommand.
# -*- coding: utf-8 -*-
"""Minimal working example for dynamic commands used as choices."""
import click
# List will later be generated somehow specific for the actual use case
allowed_input_values = ['foo', 'bar', 'foobar']
def _setup_dynamic_multicommand(allowed_input_values, _callback_function):
"""Use click.MultiCommand to achieve more or less the same."""
class DynamicCLI(click.MultiCommand):
def list_commands(self, ctx):
return allowed_input_values
def get_command(self, ctx, name):
@click.command(name)
@click.pass_context
def invoke_callback(ctx):
_callback_function(ctx)
return invoke_callback
return DynamicCLI
def _callback_function(ctx):
"""A callback that will do something with the selected command."""
sub_command = ctx.parent.invoked_subcommand
verbose = ctx.parent.params['verbose']
click.echo(f'invoked: {sub_command}')
click.echo(f'verbose: {verbose}')
# Take the input values and create a command each under the main group
dyn_multi_command = _setup_dynamic_multicommand(
allowed_input_values, _callback_function)
# A typical click command group without any other arguments or options
@click.group('minimal', cls=dyn_multi_command)
@click.option('--verbose', is_flag=True)
def main(verbose):
pass # Will be forwarded to subcommands
if __name__ == "__main__":
main() # pylint: disable=E1120
Выглядит лучше, но не уверен, что это лучшее решение.