Правильный шаблон для создания Choice-подобной опции без указания имени параметра? - PullRequest
0 голосов
/ 23 июня 2019

Для приложения я хочу создать опцию 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

Выглядит лучше, но не уверен, что это лучшее решение.

...